Skip to content

Commit a6af630

Browse files
committed
Refactor: Enforce Git URL usage and remove local wrapper dependency
로컬 wrapper 의존성을 제거하고 Git URL 사용 강제 (uvx)
1 parent 9a244bb commit a6af630

File tree

2 files changed

+178
-17
lines changed

2 files changed

+178
-17
lines changed

MCPForUnity/Editor/Helpers/AssetPathUtility.cs

Lines changed: 140 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,122 @@ public static string SanitizeAssetPath(string path)
3333
return path;
3434
}
3535

36+
/// <summary>
37+
/// Gets the absolute file system path to the package root.
38+
/// Uses StackTrace/ScriptableObject trick to resolve real path (bypassing PackageCache if symlinked).
39+
/// </summary>
40+
public static string GetPackageAbsolutePath()
41+
{
42+
// 1. Try to find path relative to this script file (Most reliable for local dev & mono scripts)
43+
try
44+
{
45+
// Get the path of THIS source file during execution
46+
string scriptFilePath = GetCallerFilePath();
47+
if (!string.IsNullOrEmpty(scriptFilePath) && File.Exists(scriptFilePath))
48+
{
49+
// Current file: .../MCPForUnity/Editor/Helpers/AssetPathUtility.cs
50+
// We need: .../MCPForUnity
51+
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(scriptFilePath), "..", ".."));
52+
}
53+
}
54+
catch (Exception ex)
55+
{
56+
McpLog.Warn($"Failed to resolve path from stack trace: {ex.Message}");
57+
}
58+
59+
// 2. Fallback to standard Unity PackageInfo (Reliable for Registry/Git packages)
60+
string packageRoot = GetMcpPackageRootPath();
61+
if (string.IsNullOrEmpty(packageRoot)) return null;
62+
63+
// If it's already an absolute path, we're good
64+
if (Path.IsPathRooted(packageRoot))
65+
{
66+
return packageRoot;
67+
}
68+
69+
// If it's a virtual path (Packages/...), resolve it to physical path
70+
if (packageRoot.StartsWith("Packages/", StringComparison.OrdinalIgnoreCase))
71+
{
72+
var packageInfo = PackageInfo.FindForAssembly(typeof(AssetPathUtility).Assembly);
73+
if (packageInfo != null && !string.IsNullOrEmpty(packageInfo.resolvedPath))
74+
{
75+
return packageInfo.resolvedPath;
76+
}
77+
78+
// If resolvedPath is failing but we have assetPath, try Path.GetFullPath
79+
// Note: This rarely works for Library/PackageCache, but worth a shot for local tarballs
80+
return Path.GetFullPath(packageRoot);
81+
}
82+
else if (packageRoot.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
83+
{
84+
string relativePath = packageRoot.Substring("Assets/".Length);
85+
return Path.Combine(Application.dataPath, relativePath);
86+
}
87+
88+
return Path.GetFullPath(packageRoot);
89+
}
90+
91+
private static string GetCallerFilePath([System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "")
92+
{
93+
return sourceFilePath;
94+
}
95+
96+
/// <summary>
97+
/// Gets the absolute path to the wrapper.js file in the package.
98+
/// </summary>
99+
/// <returns>Absolute path to wrapper.js, or null if not found</returns>
100+
public static string GetWrapperJsPath()
101+
{
102+
string packageRoot = GetMcpPackageRootPath();
103+
if (string.IsNullOrEmpty(packageRoot))
104+
{
105+
return null;
106+
}
107+
108+
// wrapper.js is expected to be in {packageRoot}/Server~/wrapper.js
109+
// But we need to handle virtual paths if consistent with GetPackageJson logic
110+
111+
string wrapperPath;
112+
113+
// Convert virtual asset path to file system path (similar logic to GetPackageJson)
114+
if (packageRoot.StartsWith("Packages/", StringComparison.OrdinalIgnoreCase))
115+
{
116+
var packageInfo = PackageInfo.FindForAssembly(typeof(AssetPathUtility).Assembly);
117+
if (packageInfo != null && !string.IsNullOrEmpty(packageInfo.resolvedPath))
118+
{
119+
wrapperPath = Path.Combine(packageInfo.resolvedPath, "Server~", "wrapper.js");
120+
}
121+
else
122+
{
123+
return null;
124+
}
125+
}
126+
else if (packageRoot.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
127+
{
128+
string relativePath = packageRoot.Substring("Assets/".Length);
129+
wrapperPath = Path.Combine(Application.dataPath, relativePath, "Server~", "wrapper.js");
130+
}
131+
else
132+
{
133+
// Already absolute or unknown
134+
wrapperPath = Path.Combine(packageRoot, "Server~", "wrapper.js");
135+
}
136+
137+
if (File.Exists(wrapperPath))
138+
{
139+
return wrapperPath;
140+
}
141+
142+
// Also check without the ~ just in case
143+
string wrapperPathNoTilde = Path.Combine(Path.GetDirectoryName(wrapperPath), "Server", "wrapper.js");
144+
if (File.Exists(wrapperPathNoTilde))
145+
{
146+
return wrapperPathNoTilde;
147+
}
148+
149+
return null;
150+
}
151+
36152
/// <summary>
37153
/// Gets the MCP for Unity package root path.
38154
/// Works for registry Package Manager, local Package Manager, and Asset Store installations.
@@ -144,22 +260,39 @@ public static JObject GetPackageJson()
144260
/// <returns>Git URL string, or empty string if version is unknown and no override</returns>
145261
public static string GetMcpServerGitUrl()
146262
{
147-
// Check for Git URL override first
263+
// Check for Git URL override first (still useful for forcing a specific repo)
148264
string gitUrlOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
149265
if (!string.IsNullOrEmpty(gitUrlOverride))
150266
{
151267
return gitUrlOverride;
152268
}
153269

154-
// Fall back to default package version
155-
string version = GetPackageVersion();
156-
if (version == "unknown")
270+
// Return hardcoded Git URL as requested by the user who prefers to push changes and use the clean URL
271+
// instead of local file paths.
272+
// Note: This means local changes MUST be pushed to GitHub to take effect in the server!
273+
return "git+https://github.com/choej2303/unity-mcp-gg.git@main#subdirectory=MCPForUnity/Server~";
274+
275+
/*
276+
// Local path logic disabled for clean config aesthetics
277+
string packageRoot = GetPackageAbsolutePath();
278+
if (!string.IsNullOrEmpty(packageRoot))
157279
{
158-
// Fall back to main repo without pinned version so configs remain valid in test scenarios
159-
return "git+https://github.com/CoplayDev/unity-mcp#subdirectory=Server";
280+
// The server code is in {packageRoot}/Server~ or {packageRoot}/Server
281+
// Try Server~ first (Unity hidden folder)
282+
string serverPathTilde = Path.Combine(packageRoot, "Server~");
283+
if (Directory.Exists(serverPathTilde))
284+
{
285+
return serverPathTilde;
286+
}
287+
288+
string serverPath = Path.Combine(packageRoot, "Server");
289+
return serverPath;
160290
}
161291
162-
return $"git+https://github.com/CoplayDev/unity-mcp@v{version}#subdirectory=Server";
292+
// Fallback to git URL if local path cannot be determined
293+
// Note: pip/uv uses '#subdirectory=' syntax, not '?path='
294+
return "git+https://github.com/choej2303/unity-mcp-gg.git@main#subdirectory=MCPForUnity/Server~";
295+
*/
163296
}
164297

165298
/// <summary>

MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,24 +78,52 @@ private static void PopulateUnityNode(JObject unity, string uvPath, McpClient cl
7878
}
7979
else
8080
{
81-
// Stdio mode: Use uvx command
81+
// Stdio mode: Use node wrapper.js
82+
// This ensures we use the python fallback logic inside wrapper.js
83+
84+
// Stdio mode: Force use of 'uvx' with Git URL directly.
8285
var (uvxPath, fromUrl, packageName) = AssetPathUtility.GetUvxCommandParts();
83-
8486
var toolArgs = BuildUvxArgs(fromUrl, packageName);
8587

86-
if (ShouldUseWindowsCmdShim(client))
88+
if (true)
8789
{
88-
unity["command"] = ResolveCmdPath();
89-
90-
var cmdArgs = new List<string> { "/c", uvxPath };
91-
cmdArgs.AddRange(toolArgs);
90+
// Use uvx directly (Git URL override or no wrapper found)
9291

93-
unity["args"] = JArray.FromObject(cmdArgs.ToArray());
92+
93+
if (ShouldUseWindowsCmdShim(client))
94+
{
95+
unity["command"] = ResolveCmdPath();
96+
97+
var cmdArgs = new List<string> { "/c", uvxPath };
98+
cmdArgs.AddRange(toolArgs);
99+
100+
unity["args"] = JArray.FromObject(cmdArgs.ToArray());
101+
}
102+
else
103+
{
104+
unity["command"] = uvxPath;
105+
unity["args"] = JArray.FromObject(toolArgs.ToArray());
106+
}
94107
}
95108
else
96109
{
97-
unity["command"] = uvxPath;
98-
unity["args"] = JArray.FromObject(toolArgs.ToArray());
110+
// Use node to run wrapper.js
111+
// We assume 'node' is in PATH unless overridden.
112+
string nodeCommand = "node";
113+
string nodeOverride = EditorPrefs.GetString(EditorPrefKeys.NodePathOverride, "");
114+
if (!string.IsNullOrEmpty(nodeOverride) && File.Exists(nodeOverride))
115+
{
116+
nodeCommand = nodeOverride;
117+
}
118+
119+
unity["command"] = nodeCommand;
120+
121+
var args = new List<string>
122+
{
123+
wrapperPath
124+
};
125+
126+
unity["args"] = JArray.FromObject(args.ToArray());
99127
}
100128

101129
// Remove url/serverUrl if they exist from previous config

0 commit comments

Comments
 (0)