Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b80b9ab
Update .Bat file and Bug fix on ManageScript
Scriptwonder Oct 25, 2025
4e9604f
Further changes
Scriptwonder Oct 25, 2025
e63096f
Merge branch 'CoplayDev:main' into main
Scriptwonder Oct 28, 2025
9827775
Merge branch 'CoplayDev:main' into main
Scriptwonder Nov 10, 2025
c5b2d35
[Custom Tool] Roslyn Runtime Compilation
Scriptwonder Nov 10, 2025
8805933
Fix based on CR
Scriptwonder Nov 10, 2025
711e8bd
Create claude_skill_unity.zip
Scriptwonder Nov 13, 2025
f37f534
Merge branch 'CoplayDev:main' into main
Scriptwonder Nov 13, 2025
8caa027
Merge branch 'CoplayDev:main' into main
Scriptwonder Nov 30, 2025
8ebb688
Merge branch 'CoplayDev:main' into main
Scriptwonder Dec 2, 2025
ae8cfe5
Update for Custom_Tool Fix and Detection
Scriptwonder Dec 2, 2025
f423c2f
Revert "Update for Custom_Tool Fix and Detection"
Scriptwonder Dec 2, 2025
b5265a7
Update README.md
Scriptwonder Dec 2, 2025
81b0f50
Reapply "Update for Custom_Tool Fix and Detection"
Scriptwonder Dec 2, 2025
9d967f5
Merge on previous PR
Scriptwonder Dec 2, 2025
5bcb6f4
Update ManageScript.cs
Scriptwonder Dec 2, 2025
ae2eedd
Update
Scriptwonder Dec 3, 2025
318c824
Update on Batch
Scriptwonder Dec 3, 2025
51fc4b4
Merge branch 'main' into batching
Scriptwonder Dec 3, 2025
55ee768
Merge pull request #1 from Scriptwonder/batching
Scriptwonder Dec 3, 2025
2127444
Revert "Merge pull request #1 from Scriptwonder/batching"
Scriptwonder Dec 3, 2025
3f03337
Merge branch 'CoplayDev:main' into main
Scriptwonder Dec 4, 2025
3b30960
Merge branch 'main' into main
Scriptwonder Dec 8, 2025
7b33aaa
Merge remote-tracking branch 'upstream/main'
Scriptwonder Dec 9, 2025
ff7972f
Updates on Camera Capture Feature
Scriptwonder Dec 9, 2025
d7e34b9
Minor changes
Scriptwonder Dec 9, 2025
1380a23
[FEATURE] Local MCPForUnity Deployment
Scriptwonder Dec 10, 2025
b263162
Merge branch 'CoplayDev:main' into main
Scriptwonder Dec 10, 2025
00317e4
Update PackageDeploymentService.cs
Scriptwonder Dec 10, 2025
e386c19
Merge branch 'main' of https://github.com/Scriptwonder/unity-mcp
Scriptwonder Dec 10, 2025
7fea8ad
Update with meta file
Scriptwonder Dec 10, 2025
941de66
Updated Readme
Scriptwonder Dec 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions MCPForUnity/Editor/Constants/EditorPrefKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ internal static class EditorPrefKeys
internal const string WebSocketUrlOverride = "MCPForUnity.WebSocketUrl";
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 UseEmbeddedServer = "MCPForUnity.UseEmbeddedServer";
internal const string LockCursorConfig = "MCPForUnity.LockCursorConfig";
Expand Down
29 changes: 29 additions & 0 deletions MCPForUnity/Editor/Services/IPackageDeploymentService.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
11 changes: 11 additions & 0 deletions MCPForUnity/Editor/Services/IPackageDeploymentService.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions MCPForUnity/Editor/Services/MCPServiceLocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public static class MCPServiceLocator
private static IToolDiscoveryService _toolDiscoveryService;
private static IServerManagementService _serverManagementService;
private static TransportManager _transportManager;
private static IPackageDeploymentService _packageDeploymentService;

public static IBridgeControlService Bridge => _bridgeService ??= new BridgeControlService();
public static IClientConfigurationService Client => _clientService ??= new ClientConfigurationService();
Expand All @@ -29,6 +30,7 @@ public static class MCPServiceLocator
public static IToolDiscoveryService ToolDiscovery => _toolDiscoveryService ??= new ToolDiscoveryService();
public static IServerManagementService Server => _serverManagementService ??= new ServerManagementService();
public static TransportManager TransportManager => _transportManager ??= new TransportManager();
public static IPackageDeploymentService Deployment => _packageDeploymentService ??= new PackageDeploymentService();

/// <summary>
/// Registers a custom implementation for a service (useful for testing)
Expand All @@ -53,6 +55,8 @@ public static void Register<T>(T implementation) where T : class
_toolDiscoveryService = td;
else if (implementation is IServerManagementService sm)
_serverManagementService = sm;
else if (implementation is IPackageDeploymentService pd)
_packageDeploymentService = pd;
else if (implementation is TransportManager tm)
_transportManager = tm;
}
Expand All @@ -71,6 +75,7 @@ public static void Reset()
(_toolDiscoveryService as IDisposable)?.Dispose();
(_serverManagementService as IDisposable)?.Dispose();
(_transportManager as IDisposable)?.Dispose();
(_packageDeploymentService as IDisposable)?.Dispose();

_bridgeService = null;
_clientService = null;
Expand All @@ -81,6 +86,7 @@ public static void Reset()
_toolDiscoveryService = null;
_serverManagementService = null;
_transportManager = null;
_packageDeploymentService = null;
}
}
}
303 changes: 303 additions & 0 deletions MCPForUnity/Editor/Services/PackageDeploymentService.cs
Original file line number Diff line number Diff line change
@@ -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
};
}
}
}
Loading