diff --git a/MCPForUnity/Editor/Constants/EditorPrefKeys.cs b/MCPForUnity/Editor/Constants/EditorPrefKeys.cs
index 8caf430..488bf39 100644
--- a/MCPForUnity/Editor/Constants/EditorPrefKeys.cs
+++ b/MCPForUnity/Editor/Constants/EditorPrefKeys.cs
@@ -54,6 +54,8 @@ namespace MCPForUnity.Editor.Constants
internal const string LastUpdateCheck = "MCPForUnity.LastUpdateCheck";
internal const string LatestKnownVersion = "MCPForUnity.LatestKnownVersion";
+ internal const string LastAssetStoreUpdateCheck = "MCPForUnity.LastAssetStoreUpdateCheck";
+ internal const string LatestKnownAssetStoreVersion = "MCPForUnity.LatestKnownAssetStoreVersion";
internal const string LastStdIoUpgradeVersion = "MCPForUnity.LastStdIoUpgradeVersion";
internal const string TelemetryDisabled = "MCPForUnity.TelemetryDisabled";
diff --git a/MCPForUnity/Editor/Services/PackageUpdateService.cs b/MCPForUnity/Editor/Services/PackageUpdateService.cs
index 91248cf..81c0161 100644
--- a/MCPForUnity/Editor/Services/PackageUpdateService.cs
+++ b/MCPForUnity/Editor/Services/PackageUpdateService.cs
@@ -8,20 +8,23 @@ using UnityEditor;
namespace MCPForUnity.Editor.Services
{
///
- /// Service for checking package updates from GitHub
+ /// Service for checking package updates from GitHub or Asset Store metadata
///
public class PackageUpdateService : IPackageUpdateService
{
private const string LastCheckDateKey = EditorPrefKeys.LastUpdateCheck;
private const string CachedVersionKey = EditorPrefKeys.LatestKnownVersion;
+ private const string LastAssetStoreCheckDateKey = EditorPrefKeys.LastAssetStoreUpdateCheck;
+ private const string CachedAssetStoreVersionKey = EditorPrefKeys.LatestKnownAssetStoreVersion;
private const string PackageJsonUrl = "https://raw.githubusercontent.com/CoplayDev/unity-mcp/main/MCPForUnity/package.json";
+ private const string AssetStoreVersionUrl = "https://gqoqjkkptwfbkwyssmnj.supabase.co/storage/v1/object/public/coplay-images/assetstoreversion.json";
///
public UpdateCheckResult CheckForUpdate(string currentVersion)
{
- // Check cache first - only check once per day
- string lastCheckDate = EditorPrefs.GetString(LastCheckDateKey, "");
- string cachedLatestVersion = EditorPrefs.GetString(CachedVersionKey, "");
+ bool isGitInstallation = IsGitInstallation();
+ string lastCheckDate = EditorPrefs.GetString(isGitInstallation ? LastCheckDateKey : LastAssetStoreCheckDateKey, "");
+ string cachedLatestVersion = EditorPrefs.GetString(isGitInstallation ? CachedVersionKey : CachedAssetStoreVersionKey, "");
if (lastCheckDate == DateTime.Now.ToString("yyyy-MM-dd") && !string.IsNullOrEmpty(cachedLatestVersion))
{
@@ -34,25 +37,15 @@ namespace MCPForUnity.Editor.Services
};
}
- // Don't check for Asset Store installations
- if (!IsGitInstallation())
- {
- return new UpdateCheckResult
- {
- CheckSucceeded = false,
- UpdateAvailable = false,
- Message = "Asset Store installations are updated via Unity Asset Store"
- };
- }
-
- // Fetch latest version from GitHub
- string latestVersion = FetchLatestVersionFromGitHub();
+ string latestVersion = isGitInstallation
+ ? FetchLatestVersionFromGitHub()
+ : FetchLatestVersionFromAssetStoreJson();
if (!string.IsNullOrEmpty(latestVersion))
{
// Cache the result
- EditorPrefs.SetString(LastCheckDateKey, DateTime.Now.ToString("yyyy-MM-dd"));
- EditorPrefs.SetString(CachedVersionKey, latestVersion);
+ EditorPrefs.SetString(isGitInstallation ? LastCheckDateKey : LastAssetStoreCheckDateKey, DateTime.Now.ToString("yyyy-MM-dd"));
+ EditorPrefs.SetString(isGitInstallation ? CachedVersionKey : CachedAssetStoreVersionKey, latestVersion);
return new UpdateCheckResult
{
@@ -67,7 +60,9 @@ namespace MCPForUnity.Editor.Services
{
CheckSucceeded = false,
UpdateAvailable = false,
- Message = "Failed to check for updates (network issue or offline)"
+ Message = isGitInstallation
+ ? "Failed to check for updates (network issue or offline)"
+ : "Failed to check for Asset Store updates (network issue or offline)"
};
}
@@ -101,7 +96,7 @@ namespace MCPForUnity.Editor.Services
}
///
- public bool IsGitInstallation()
+ public virtual bool IsGitInstallation()
{
// Git packages are installed via Package Manager and have a package.json in Packages/
// Asset Store packages are in Assets/
@@ -122,12 +117,14 @@ namespace MCPForUnity.Editor.Services
{
EditorPrefs.DeleteKey(LastCheckDateKey);
EditorPrefs.DeleteKey(CachedVersionKey);
+ EditorPrefs.DeleteKey(LastAssetStoreCheckDateKey);
+ EditorPrefs.DeleteKey(CachedAssetStoreVersionKey);
}
///
/// Fetches the latest version from GitHub's main branch package.json
///
- private string FetchLatestVersionFromGitHub()
+ protected virtual string FetchLatestVersionFromGitHub()
{
try
{
@@ -158,5 +155,31 @@ namespace MCPForUnity.Editor.Services
return null;
}
}
+
+ ///
+ /// Fetches the latest Asset Store version from a hosted JSON file.
+ ///
+ protected virtual string FetchLatestVersionFromAssetStoreJson()
+ {
+ try
+ {
+ using (var client = new WebClient())
+ {
+ client.Headers.Add("User-Agent", "Unity-MCPForUnity-AssetStoreUpdateChecker");
+ string jsonContent = client.DownloadString(AssetStoreVersionUrl);
+
+ var versionJson = JObject.Parse(jsonContent);
+ string version = versionJson["version"]?.ToString();
+
+ return string.IsNullOrEmpty(version) ? null : version;
+ }
+ }
+ catch (Exception ex)
+ {
+ // Silent fail - don't interrupt the user if network is unavailable
+ McpLog.Info($"Asset Store update check failed (this is normal if offline): {ex.Message}");
+ return null;
+ }
+ }
}
}
diff --git a/MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs b/MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs
index 55bbbec..9a2b86a 100644
--- a/MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs
+++ b/MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs
@@ -48,8 +48,10 @@ namespace MCPForUnity.Editor.Windows
// Integer prefs
{ EditorPrefKeys.UnitySocketPort, EditorPrefType.Int },
{ EditorPrefKeys.ValidationLevel, EditorPrefType.Int },
- { EditorPrefKeys.LastUpdateCheck, EditorPrefType.Int },
+ { EditorPrefKeys.LastUpdateCheck, EditorPrefType.String },
{ EditorPrefKeys.LastStdIoUpgradeVersion, EditorPrefType.Int },
+ { EditorPrefKeys.LastLocalHttpServerPid, EditorPrefType.Int },
+ { EditorPrefKeys.LastLocalHttpServerPort, EditorPrefType.Int },
// String prefs
{ EditorPrefKeys.EditorWindowActivePanel, EditorPrefType.String },
@@ -67,6 +69,12 @@ namespace MCPForUnity.Editor.Windows
{ EditorPrefKeys.PackageDeployLastSourcePath, EditorPrefType.String },
{ EditorPrefKeys.ServerSrc, EditorPrefType.String },
{ EditorPrefKeys.LatestKnownVersion, EditorPrefType.String },
+ { EditorPrefKeys.LastAssetStoreUpdateCheck, EditorPrefType.String },
+ { EditorPrefKeys.LatestKnownAssetStoreVersion, EditorPrefType.String },
+ { EditorPrefKeys.LastLocalHttpServerStartedUtc, EditorPrefType.String },
+ { EditorPrefKeys.LastLocalHttpServerPidArgsHash, EditorPrefType.String },
+ { EditorPrefKeys.LastLocalHttpServerPidFilePath, EditorPrefType.String },
+ { EditorPrefKeys.LastLocalHttpServerInstanceToken, EditorPrefType.String },
};
// Templates
diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/PackageUpdateServiceTests.cs b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/PackageUpdateServiceTests.cs
index 2d60063..1eced45 100644
--- a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/PackageUpdateServiceTests.cs
+++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/PackageUpdateServiceTests.cs
@@ -11,6 +11,8 @@ namespace MCPForUnityTests.Editor.Services
private PackageUpdateService _service;
private const string TestLastCheckDateKey = EditorPrefKeys.LastUpdateCheck;
private const string TestCachedVersionKey = EditorPrefKeys.LatestKnownVersion;
+ private const string TestAssetStoreLastCheckDateKey = EditorPrefKeys.LastAssetStoreUpdateCheck;
+ private const string TestAssetStoreCachedVersionKey = EditorPrefKeys.LatestKnownAssetStoreVersion;
[SetUp]
public void SetUp()
@@ -38,6 +40,14 @@ namespace MCPForUnityTests.Editor.Services
{
EditorPrefs.DeleteKey(TestCachedVersionKey);
}
+ if (EditorPrefs.HasKey(TestAssetStoreLastCheckDateKey))
+ {
+ EditorPrefs.DeleteKey(TestAssetStoreLastCheckDateKey);
+ }
+ if (EditorPrefs.HasKey(TestAssetStoreCachedVersionKey))
+ {
+ EditorPrefs.DeleteKey(TestAssetStoreCachedVersionKey);
+ }
}
[Test]
@@ -211,23 +221,73 @@ namespace MCPForUnityTests.Editor.Services
}
[Test]
- public void CheckForUpdate_ReturnsAssetStoreMessage_ForNonGitInstallations()
+ public void CheckForUpdate_UsesAssetStoreCache_WhenCacheIsValid()
{
- // Note: This test verifies the service behavior when IsGitInstallation() returns false.
- // Since the actual result depends on package installation method, we create a mock
- // implementation to test this specific code path.
-
- var mockService = new MockAssetStorePackageUpdateService();
-
+ // Arrange: Set up valid Asset Store cache
+ string today = DateTime.Now.ToString("yyyy-MM-dd");
+ string cachedVersion = "9.0.1";
+ EditorPrefs.SetString(TestAssetStoreLastCheckDateKey, today);
+ EditorPrefs.SetString(TestAssetStoreCachedVersionKey, cachedVersion);
+
+ var mockService = new TestablePackageUpdateService
+ {
+ IsGitInstallationResult = false,
+ AssetStoreFetchResult = "9.9.9"
+ };
+
// Act
- var result = mockService.CheckForUpdate("5.0.0");
+ var result = mockService.CheckForUpdate("9.0.0");
// Assert
- Assert.IsFalse(result.CheckSucceeded, "Check should not succeed for Asset Store installs");
- Assert.IsFalse(result.UpdateAvailable, "No update should be reported for Asset Store installs");
- Assert.AreEqual("Asset Store installations are updated via Unity Asset Store", result.Message,
- "Should return Asset Store update message");
- Assert.IsNull(result.LatestVersion, "Latest version should be null for Asset Store installs");
+ Assert.IsTrue(result.CheckSucceeded, "Check should succeed with valid Asset Store cache");
+ Assert.AreEqual(cachedVersion, result.LatestVersion, "Should return cached Asset Store version");
+ Assert.IsTrue(result.UpdateAvailable, "Update should be available (9.0.1 > 9.0.0)");
+ Assert.IsFalse(mockService.AssetStoreFetchCalled, "Should not fetch when Asset Store cache is valid");
+ }
+
+ [Test]
+ public void CheckForUpdate_FetchesAssetStoreJson_WhenCacheExpired()
+ {
+ // Arrange: Set expired Asset Store cache and a valid Git cache to ensure separation
+ string yesterday = DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd");
+ EditorPrefs.SetString(TestAssetStoreLastCheckDateKey, yesterday);
+ EditorPrefs.SetString(TestAssetStoreCachedVersionKey, "9.0.0");
+ EditorPrefs.SetString(TestLastCheckDateKey, DateTime.Now.ToString("yyyy-MM-dd"));
+ EditorPrefs.SetString(TestCachedVersionKey, "99.0.0");
+
+ var mockService = new TestablePackageUpdateService
+ {
+ IsGitInstallationResult = false,
+ AssetStoreFetchResult = "9.1.0"
+ };
+
+ // Act
+ var result = mockService.CheckForUpdate("9.0.0");
+
+ // Assert
+ Assert.IsTrue(result.CheckSucceeded, "Check should succeed when fetch returns a version");
+ Assert.AreEqual("9.1.0", result.LatestVersion, "Should use fetched Asset Store version");
+ Assert.IsTrue(mockService.AssetStoreFetchCalled, "Should fetch when Asset Store cache is expired");
+ }
+
+ [Test]
+ public void CheckForUpdate_ReturnsAssetStoreFailureMessage_WhenFetchFails()
+ {
+ // Arrange
+ var mockService = new TestablePackageUpdateService
+ {
+ IsGitInstallationResult = false,
+ AssetStoreFetchResult = null
+ };
+
+ // Act
+ var result = mockService.CheckForUpdate("9.0.0");
+
+ // Assert
+ Assert.IsFalse(result.CheckSucceeded, "Check should fail when Asset Store fetch fails");
+ Assert.IsFalse(result.UpdateAvailable, "No update should be reported when fetch fails");
+ Assert.AreEqual("Failed to check for Asset Store updates (network issue or offline)", result.Message);
+ Assert.IsNull(result.LatestVersion, "Latest version should be null when fetch fails");
}
[Test]
@@ -236,10 +296,14 @@ namespace MCPForUnityTests.Editor.Services
// Arrange: Set up cache
EditorPrefs.SetString(TestLastCheckDateKey, DateTime.Now.ToString("yyyy-MM-dd"));
EditorPrefs.SetString(TestCachedVersionKey, "5.0.0");
+ EditorPrefs.SetString(TestAssetStoreLastCheckDateKey, DateTime.Now.ToString("yyyy-MM-dd"));
+ EditorPrefs.SetString(TestAssetStoreCachedVersionKey, "9.0.0");
// Verify cache exists
Assert.IsTrue(EditorPrefs.HasKey(TestLastCheckDateKey), "Cache should exist before clearing");
Assert.IsTrue(EditorPrefs.HasKey(TestCachedVersionKey), "Cache should exist before clearing");
+ Assert.IsTrue(EditorPrefs.HasKey(TestAssetStoreLastCheckDateKey), "Asset Store cache should exist before clearing");
+ Assert.IsTrue(EditorPrefs.HasKey(TestAssetStoreCachedVersionKey), "Asset Store cache should exist before clearing");
// Act
_service.ClearCache();
@@ -247,6 +311,8 @@ namespace MCPForUnityTests.Editor.Services
// Assert
Assert.IsFalse(EditorPrefs.HasKey(TestLastCheckDateKey), "Date cache should be cleared");
Assert.IsFalse(EditorPrefs.HasKey(TestCachedVersionKey), "Version cache should be cleared");
+ Assert.IsFalse(EditorPrefs.HasKey(TestAssetStoreLastCheckDateKey), "Asset Store date cache should be cleared");
+ Assert.IsFalse(EditorPrefs.HasKey(TestAssetStoreCachedVersionKey), "Asset Store version cache should be cleared");
}
[Test]
@@ -261,36 +327,31 @@ namespace MCPForUnityTests.Editor.Services
}
///
- /// Mock implementation of IPackageUpdateService that simulates Asset Store installation behavior
+ /// Testable implementation that allows forcing install type and fetch results.
///
- internal class MockAssetStorePackageUpdateService : IPackageUpdateService
+ internal class TestablePackageUpdateService : PackageUpdateService
{
- public UpdateCheckResult CheckForUpdate(string currentVersion)
+ public bool IsGitInstallationResult { get; set; } = true;
+ public string GitFetchResult { get; set; }
+ public string AssetStoreFetchResult { get; set; }
+ public bool GitFetchCalled { get; private set; }
+ public bool AssetStoreFetchCalled { get; private set; }
+
+ public override bool IsGitInstallation()
{
- // Simulate Asset Store installation (IsGitInstallation returns false)
- return new UpdateCheckResult
- {
- CheckSucceeded = false,
- UpdateAvailable = false,
- Message = "Asset Store installations are updated via Unity Asset Store"
- };
+ return IsGitInstallationResult;
}
- public bool IsNewerVersion(string version1, string version2)
+ protected override string FetchLatestVersionFromGitHub()
{
- // Not used in the Asset Store test, but required by interface
- return false;
+ GitFetchCalled = true;
+ return GitFetchResult;
}
- public bool IsGitInstallation()
+ protected override string FetchLatestVersionFromAssetStoreJson()
{
- // Simulate non-Git installation (Asset Store)
- return false;
- }
-
- public void ClearCache()
- {
- // Not used in the Asset Store test, but required by interface
+ AssetStoreFetchCalled = true;
+ return AssetStoreFetchResult;
}
}
}
diff --git a/tools/prepare_unity_asset_store_release.py b/tools/prepare_unity_asset_store_release.py
index b8dda3d..2b80fe2 100644
--- a/tools/prepare_unity_asset_store_release.py
+++ b/tools/prepare_unity_asset_store_release.py
@@ -1,4 +1,12 @@
#!/usr/bin/env python3
+"""Prepare MCPForUnity for Asset Store upload.
+
+Usage:
+ python tools/prepare_unity_asset_store_release.py \
+ --remote-url https://your.remote.endpoint/ \
+ --asset-project /path/to/AssetStoreUploads \
+ --backup
+"""
from __future__ import annotations
import argparse
@@ -73,6 +81,11 @@ def main() -> int:
default=None,
help="Path to the Unity project used for Asset Store uploads.",
)
+ parser.add_argument(
+ "--remote-url",
+ required=True,
+ help="Remote MCP HTTP base URL to set as default for Asset Store builds.",
+ )
parser.add_argument(
"--backup",
action="store_true",
@@ -88,6 +101,9 @@ def main() -> int:
repo_root = Path(args.repo_root).expanduser().resolve()
asset_project = Path(args.asset_project).expanduser().resolve(
) if args.asset_project else (repo_root / "TestProjects" / "AssetStoreUploads")
+ remote_url = args.remote_url.strip()
+ if not remote_url:
+ raise RuntimeError("--remote-url must be a non-empty URL")
source_mcp = repo_root / "MCPForUnity"
if not source_mcp.is_dir():
@@ -125,11 +141,11 @@ def main() -> int:
# Remove auto-popup setup window for Asset Store packaging
remove_line_exact(setup_service, "[InitializeOnLoad]")
- # Set default base URL to the hosted endpoint
+ # Set default remote base URL to the hosted endpoint
replace_once(
http_util,
- r'private const string DefaultBaseUrl = "http://localhost:8080";',
- 'private const string DefaultBaseUrl = "https://mc-0cb5e1039f6b4499b473670f70662d29.ecs.us-east-2.on.aws/";',
+ r'private const string DefaultRemoteBaseUrl = "";',
+ f'private const string DefaultRemoteBaseUrl = "{remote_url}";',
)
# Default transport to HTTP Remote and persist inferred scope when missing
@@ -138,6 +154,11 @@ def main() -> int:
r'transportDropdown\.Init\(TransportProtocol\.HTTPLocal\);',
'transportDropdown.Init(TransportProtocol.HTTPRemote);',
)
+ replace_once(
+ connection_section,
+ r'scope = MCPServiceLocator\.Server\.IsLocalUrl\(\) \? "local" : "remote";',
+ 'scope = "remote";',
+ )
# 2) Replace Assets/MCPForUnity in the target project
if dest_mcp.exists():