unity-mcp/MCPForUnity/Editor/Helpers/AssetPathUtility.cs

163 lines
6.2 KiB
C#
Raw Normal View History

using System;
New UI and work without MCP server embedded (#313) * First pass at new UI * Point to new UI * Refactor: New Service-Based MCP Editor Window Architecture We separate the business logic from the UI rendering of the new editor window with new services. I didn't go full Dependency Injection, not sure if I want to add those deps to the install as yet, but service location is fairly straightforward. Some differences with the old window: - No more Auto-Setup, users will manually decide what they want to do - Removed Python detection warning, we have a setup wizard now - Added explicit path overrides for `uv` and the MCP server itself * style: add flex-shrink and overflow handling to improve UI element scaling * fix: update UI configuration and visibility when client status changes * feat: add menu item to open legacy MCP configuration window * refactor: improve editor window lifecycle handling with proper update subscription * feat: add auto-verification of bridge health when connected * fix: update Claude Code MCP server registration to use lowercase unityMCP name and correct the manual installation instructions * fix: add Claude CLI directory to PATH for node/nvm environments * Clarify how users will see MCP tools * Add a keyboard shortcut to open the window * feat: add server download UI and improve installation status messaging This is needed for the Unity Asset Store, which doesn't have the Python server embedded. * feat: add dynamic asset path detection to support both Package Manager and Asset Store installations * fix: replace unicode emojis with escaped characters in status messages * feat: add server package creation and GitHub release publishing to version bump workflow * fix: add v prefix to server package filename in release workflow * Fix download location * style: improve dropdown and settings layout responsiveness with flex-shrink and max-width * feat: add package.json version detection and refactor path utilities * refactor: simplify imports and use fully qualified names in ServerInstaller.cs * refactor: replace Unity Debug.Log calls with custom McpLog class * fix: extract server files to temp directory before moving to final location * docs: add v6 UI documentation and screenshots with service architecture overview * docs: add new UI Toolkit-based editor window with service architecture and path overrides * feat: improve package path resolution to support Package Manager and Asset Store installations * Change Claude Code's casing back to "UnityMCP" There's no need to break anything as yet * fix: update success dialog text to clarify manual bridge start requirement * refactor: move RefreshDebounce and ManageScriptRefreshHelpers classes inside namespace * feat: add Asset Store fallback path detection for package root lookup * fix: update server installation success message to be more descriptive * refactor: replace Unity Debug.Log calls with custom McpLog utility * fix: add file existence check before opening configuration file * refactor: simplify asset path handling and remove redundant helper namespace references * docs: update code block syntax highlighting in UI changes doc * docs: add code block syntax highlighting for file structure example * feat: import UnityEditor.UIElements namespace for UI components for Unity 2021 compatibility * refactor: rename Python server references to MCP server for consistency * fix: reset client status label color after error state is cleared * Replace the phrase "Python server" with "MCP server" * MInor doc clarification * docs: add path override methods for UV and Claude CLI executables * docs: update service locator registration method name from SetCustomImplementation to Register
2025-10-11 15:08:16 +08:00
using System.IO;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using PackageInfo = UnityEditor.PackageManager.PackageInfo;
namespace MCPForUnity.Editor.Helpers
{
/// <summary>
/// Provides common utility methods for working with Unity asset paths.
/// </summary>
public static class AssetPathUtility
{
/// <summary>
/// Normalizes a Unity asset path by ensuring forward slashes are used and that it is rooted under "Assets/".
/// </summary>
public static string SanitizeAssetPath(string path)
{
if (string.IsNullOrEmpty(path))
{
return path;
}
path = path.Replace('\\', '/');
if (!path.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
{
return "Assets/" + path.TrimStart('/');
}
return path;
}
New UI and work without MCP server embedded (#313) * First pass at new UI * Point to new UI * Refactor: New Service-Based MCP Editor Window Architecture We separate the business logic from the UI rendering of the new editor window with new services. I didn't go full Dependency Injection, not sure if I want to add those deps to the install as yet, but service location is fairly straightforward. Some differences with the old window: - No more Auto-Setup, users will manually decide what they want to do - Removed Python detection warning, we have a setup wizard now - Added explicit path overrides for `uv` and the MCP server itself * style: add flex-shrink and overflow handling to improve UI element scaling * fix: update UI configuration and visibility when client status changes * feat: add menu item to open legacy MCP configuration window * refactor: improve editor window lifecycle handling with proper update subscription * feat: add auto-verification of bridge health when connected * fix: update Claude Code MCP server registration to use lowercase unityMCP name and correct the manual installation instructions * fix: add Claude CLI directory to PATH for node/nvm environments * Clarify how users will see MCP tools * Add a keyboard shortcut to open the window * feat: add server download UI and improve installation status messaging This is needed for the Unity Asset Store, which doesn't have the Python server embedded. * feat: add dynamic asset path detection to support both Package Manager and Asset Store installations * fix: replace unicode emojis with escaped characters in status messages * feat: add server package creation and GitHub release publishing to version bump workflow * fix: add v prefix to server package filename in release workflow * Fix download location * style: improve dropdown and settings layout responsiveness with flex-shrink and max-width * feat: add package.json version detection and refactor path utilities * refactor: simplify imports and use fully qualified names in ServerInstaller.cs * refactor: replace Unity Debug.Log calls with custom McpLog class * fix: extract server files to temp directory before moving to final location * docs: add v6 UI documentation and screenshots with service architecture overview * docs: add new UI Toolkit-based editor window with service architecture and path overrides * feat: improve package path resolution to support Package Manager and Asset Store installations * Change Claude Code's casing back to "UnityMCP" There's no need to break anything as yet * fix: update success dialog text to clarify manual bridge start requirement * refactor: move RefreshDebounce and ManageScriptRefreshHelpers classes inside namespace * feat: add Asset Store fallback path detection for package root lookup * fix: update server installation success message to be more descriptive * refactor: replace Unity Debug.Log calls with custom McpLog utility * fix: add file existence check before opening configuration file * refactor: simplify asset path handling and remove redundant helper namespace references * docs: update code block syntax highlighting in UI changes doc * docs: add code block syntax highlighting for file structure example * feat: import UnityEditor.UIElements namespace for UI components for Unity 2021 compatibility * refactor: rename Python server references to MCP server for consistency * fix: reset client status label color after error state is cleared * Replace the phrase "Python server" with "MCP server" * MInor doc clarification * docs: add path override methods for UV and Claude CLI executables * docs: update service locator registration method name from SetCustomImplementation to Register
2025-10-11 15:08:16 +08:00
/// <summary>
/// Gets the MCP for Unity package root path.
/// Works for registry Package Manager, local Package Manager, and Asset Store installations.
/// </summary>
/// <returns>The package root path (virtual for PM, absolute for Asset Store), or null if not found</returns>
public static string GetMcpPackageRootPath()
{
try
{
// Try Package Manager first (registry and local installs)
var packageInfo = PackageInfo.FindForAssembly(typeof(AssetPathUtility).Assembly);
if (packageInfo != null && !string.IsNullOrEmpty(packageInfo.assetPath))
{
return packageInfo.assetPath;
}
// Fallback to AssetDatabase for Asset Store installs (Assets/MCPForUnity)
string[] guids = AssetDatabase.FindAssets($"t:Script {nameof(AssetPathUtility)}");
if (guids.Length == 0)
{
McpLog.Warn("Could not find AssetPathUtility script in AssetDatabase");
return null;
}
string scriptPath = AssetDatabase.GUIDToAssetPath(guids[0]);
// Script is at: {packageRoot}/Editor/Helpers/AssetPathUtility.cs
// Extract {packageRoot}
int editorIndex = scriptPath.IndexOf("/Editor/", StringComparison.Ordinal);
if (editorIndex >= 0)
{
return scriptPath.Substring(0, editorIndex);
}
McpLog.Warn($"Could not determine package root from script path: {scriptPath}");
return null;
}
catch (Exception ex)
{
McpLog.Error($"Failed to get package root path: {ex.Message}");
return null;
}
}
/// <summary>
/// Reads and parses the package.json file for MCP for Unity.
/// Handles both Package Manager (registry/local) and Asset Store installations.
/// </summary>
/// <returns>JObject containing package.json data, or null if not found or parse failed</returns>
public static JObject GetPackageJson()
{
try
{
string packageRoot = GetMcpPackageRootPath();
if (string.IsNullOrEmpty(packageRoot))
{
return null;
}
string packageJsonPath = Path.Combine(packageRoot, "package.json");
// Convert virtual asset path to file system path
if (packageRoot.StartsWith("Packages/", StringComparison.OrdinalIgnoreCase))
{
// Package Manager install - must use PackageInfo.resolvedPath
// Virtual paths like "Packages/..." don't work with File.Exists()
// Registry packages live in Library/PackageCache/package@version/
var packageInfo = PackageInfo.FindForAssembly(typeof(AssetPathUtility).Assembly);
if (packageInfo != null && !string.IsNullOrEmpty(packageInfo.resolvedPath))
{
packageJsonPath = Path.Combine(packageInfo.resolvedPath, "package.json");
}
else
{
McpLog.Warn("Could not resolve Package Manager path for package.json");
return null;
}
}
else if (packageRoot.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
{
// Asset Store install - convert to absolute file system path
// Application.dataPath is the absolute path to the Assets folder
string relativePath = packageRoot.Substring("Assets/".Length);
packageJsonPath = Path.Combine(Application.dataPath, relativePath, "package.json");
}
if (!File.Exists(packageJsonPath))
{
McpLog.Warn($"package.json not found at: {packageJsonPath}");
return null;
}
string json = File.ReadAllText(packageJsonPath);
return JObject.Parse(json);
}
catch (Exception ex)
{
McpLog.Warn($"Failed to read or parse package.json: {ex.Message}");
return null;
}
}
/// <summary>
/// Gets the version string from the package.json file.
/// </summary>
/// <returns>Version string, or "unknown" if not found</returns>
public static string GetPackageVersion()
{
try
{
var packageJson = GetPackageJson();
if (packageJson == null)
{
return "unknown";
}
string version = packageJson["version"]?.ToString();
return string.IsNullOrEmpty(version) ? "unknown" : version;
}
catch (Exception ex)
{
McpLog.Warn($"Failed to get package version: {ex.Message}");
return "unknown";
}
}
}
}