Skip to content

Commit 2e93b69

Browse files
authored
Merge pull request #1084 from Scriptwonder/chore/issue-fixes-2026-04-26
Patch-Fix-04-26
2 parents 3adb569 + 996ccd1 commit 2e93b69

10 files changed

Lines changed: 157 additions & 54 deletions

MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public class OpenCodeConfigurator : McpClientConfiguratorBase
1616
{
1717
private const string ServerName = "unityMCP";
1818
private const string SchemaUrl = "https://opencode.ai/config.json";
19+
private const string RemoteType = "remote";
20+
private const string LocalType = "local";
1921

2022
public OpenCodeConfigurator() : base(new McpClient
2123
{
@@ -96,10 +98,7 @@ public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
9698
return client.status;
9799
}
98100

99-
string configuredUrl = unityMcp["url"]?.ToString();
100-
string expectedUrl = HttpEndpointUtility.GetMcpRpcUrl();
101-
102-
if (UrlsEqual(configuredUrl, expectedUrl))
101+
if (EntryMatchesCurrentTransport(unityMcp))
103102
{
104103
client.SetStatus(McpStatus.Configured);
105104
}
@@ -168,11 +167,58 @@ public override string GetManualSnippet()
168167
"The Unity MCP server should be detected automatically"
169168
};
170169

171-
private static JObject BuildServerEntry() => new JObject
170+
private static JObject BuildServerEntry()
172171
{
173-
["type"] = "remote",
174-
["url"] = HttpEndpointUtility.GetMcpRpcUrl(),
175-
["enabled"] = true
176-
};
172+
if (HttpEndpointUtility.GetCurrentServerTransport() == ConfiguredTransport.Stdio)
173+
{
174+
var (uvxPath, _, packageName) = AssetPathUtility.GetUvxCommandParts();
175+
if (string.IsNullOrWhiteSpace(uvxPath))
176+
{
177+
throw new InvalidOperationException("uvx not found. Install uv/uvx or set the override in Advanced Settings.");
178+
}
179+
180+
var command = new JArray { uvxPath };
181+
foreach (string value in AssetPathUtility.GetUvxDevFlagsList())
182+
{
183+
command.Add(value);
184+
}
185+
foreach (string value in AssetPathUtility.GetBetaServerFromArgsList())
186+
{
187+
command.Add(value);
188+
}
189+
command.Add(packageName);
190+
command.Add("--transport");
191+
command.Add("stdio");
192+
193+
return new JObject
194+
{
195+
["type"] = LocalType,
196+
["command"] = command,
197+
["enabled"] = true
198+
};
199+
}
200+
201+
return new JObject
202+
{
203+
["type"] = RemoteType,
204+
["url"] = HttpEndpointUtility.GetMcpRpcUrl(),
205+
["enabled"] = true
206+
};
207+
}
208+
209+
private bool EntryMatchesCurrentTransport(JObject entry)
210+
{
211+
string entryType = entry["type"]?.ToString();
212+
ConfiguredTransport expected = HttpEndpointUtility.GetCurrentServerTransport();
213+
214+
if (expected == ConfiguredTransport.Stdio)
215+
{
216+
return string.Equals(entryType, LocalType, StringComparison.OrdinalIgnoreCase)
217+
&& entry["command"] is JArray;
218+
}
219+
220+
return string.Equals(entryType, RemoteType, StringComparison.OrdinalIgnoreCase)
221+
&& UrlsEqual(entry["url"]?.ToString(), HttpEndpointUtility.GetMcpRpcUrl());
222+
}
177223
}
178224
}

MCPForUnity/Editor/Helpers/GameObjectLookup.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,7 @@ public static GameObject FindByTarget(JToken target, string searchMethod, bool i
7070
/// </summary>
7171
public static UnityEngine.Object ResolveInstanceID(int instanceId)
7272
{
73-
#if UNITY_6000_3_OR_NEWER
74-
return EditorUtility.EntityIdToObject(instanceId);
75-
#else
76-
return EditorUtility.InstanceIDToObject(instanceId);
77-
#endif
73+
return UnityObjectIdCompat.InstanceIDToObjectCompat(instanceId);
7874
}
7975

8076
/// <summary>

MCPForUnity/Editor/Helpers/HttpEndpointUtility.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,17 @@ private static string NormalizeBaseUrl(string value, string defaultUrl, bool rem
336336
trimmed = trimmed[..^4];
337337
}
338338

339+
// For local scope, force 127.0.0.1 over "localhost". Windows getaddrinfo returns ::1
340+
// first; clients without Happy Eyeballs (e.g., Codex/reqwest) hit the v6 socket while
341+
// our server binds v4-only, breaking the handshake. The default server bind is
342+
// 127.0.0.1, so emitting the literal v4 keeps every client unambiguous.
343+
if (!remoteScope && Uri.TryCreate(trimmed, UriKind.Absolute, out Uri parsed)
344+
&& string.Equals(parsed.Host, "localhost", StringComparison.OrdinalIgnoreCase))
345+
{
346+
var builder = new UriBuilder(parsed) { Host = "127.0.0.1" };
347+
trimmed = builder.Uri.GetLeftPart(UriPartial.Authority);
348+
}
349+
339350
return trimmed;
340351
}
341352

MCPForUnity/Editor/Helpers/McpLogRecord.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,22 @@ internal static class McpLogRecord
1616
private const long MaxLogSizeBytes = 1024 * 1024; // 1 MB
1717
private static bool _sessionStarted;
1818
private static readonly object _logLock = new();
19+
private static volatile bool _isEnabledCached;
20+
21+
[InitializeOnLoadMethod]
22+
private static void RefreshFromPrefs()
23+
{
24+
_isEnabledCached = EditorPrefs.GetBool(EditorPrefKeys.LogRecordEnabled, false);
25+
}
1926

2027
internal static bool IsEnabled
2128
{
22-
get => EditorPrefs.GetBool(EditorPrefKeys.LogRecordEnabled, false);
23-
set => EditorPrefs.SetBool(EditorPrefKeys.LogRecordEnabled, value);
29+
get => _isEnabledCached;
30+
set
31+
{
32+
EditorPrefs.SetBool(EditorPrefKeys.LogRecordEnabled, value);
33+
_isEnabledCached = value;
34+
}
2435
}
2536

2637
internal static void Log(string commandType, JObject parameters, string type, string status, long durationMs, string error = null)

MCPForUnity/Editor/MCPForUnity.Editor.asmdef

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"Editor"
99
],
1010
"excludePlatforms": [],
11-
"overrideReferences": false,
11+
"overrideReferences": true,
1212
"precompiledReferences": [
1313
"Newtonsoft.Json.dll"
1414
],
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using UnityEngine;
2+
#if UNITY_EDITOR
3+
using System.Reflection;
4+
using UnityEditor;
5+
#endif
6+
7+
namespace MCPForUnity.Runtime.Helpers
8+
{
9+
/// <summary>
10+
/// Version-gated wrappers for the InstanceID ↔ EntityId migration introduced in Unity 6.5
11+
/// and tightened in 6.6.
12+
/// Forward (Object → int): <see cref="GetInstanceIDCompat"/>
13+
/// Reverse (int → Object, Editor-only): <see cref="InstanceIDToObjectCompat"/>
14+
/// </summary>
15+
public static class UnityObjectIdCompat
16+
{
17+
/// <summary>
18+
/// Returns a session-scoped int handle for the object. On 6.5+ truncates the
19+
/// EntityId's underlying ulong; lossy but stable within a session and preserves
20+
/// the int-shaped wire format that older consumers expect. For deserialization
21+
/// round-trips on 6.5+, prefer the full <c>entityID</c> field.
22+
/// </summary>
23+
public static int GetInstanceIDCompat(this Object obj)
24+
{
25+
if (obj == null)
26+
{
27+
return 0;
28+
}
29+
30+
#if UNITY_6000_5_OR_NEWER
31+
return (int)EntityId.ToULong(obj.GetEntityId());
32+
#else
33+
return obj.GetInstanceID();
34+
#endif
35+
}
36+
37+
#if UNITY_EDITOR
38+
#if UNITY_6000_6_OR_NEWER
39+
private static MethodInfo _instanceIdToObject;
40+
private static bool _instanceIdToObjectInitialized;
41+
#endif
42+
43+
/// <summary>
44+
/// Resolves an int instance ID handle back to a UnityEngine.Object.
45+
/// Pre-6.0 : EditorUtility.InstanceIDToObject(int)
46+
/// 6.0–6.5 : EditorUtility.EntityIdToObject(int) (implicit int→EntityId cast)
47+
/// 6.6+ : reflection on InstanceIDToObject(int) — the API still exists at runtime
48+
/// but is obsolete-as-error; reflection bypasses CS0619 until the public
49+
/// EntityId(int) ctor stabilizes.
50+
/// </summary>
51+
public static Object InstanceIDToObjectCompat(int instanceId)
52+
{
53+
#if UNITY_6000_6_OR_NEWER
54+
if (!_instanceIdToObjectInitialized)
55+
{
56+
_instanceIdToObject = typeof(EditorUtility).GetMethod(
57+
"InstanceIDToObject",
58+
BindingFlags.Public | BindingFlags.Static,
59+
null,
60+
new[] { typeof(int) },
61+
null);
62+
_instanceIdToObjectInitialized = true;
63+
}
64+
return _instanceIdToObject?.Invoke(null, new object[] { instanceId }) as Object;
65+
#elif UNITY_6000_3_OR_NEWER
66+
return EditorUtility.EntityIdToObject(instanceId);
67+
#else
68+
return EditorUtility.InstanceIDToObject(instanceId);
69+
#endif
70+
}
71+
#endif
72+
}
73+
}

MCPForUnity/Runtime/Helpers/UnityObjectIdCompat.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

MCPForUnity/Runtime/Helpers/UnityObjectIdCompatExtensions.cs

Lines changed: 0 additions & 30 deletions
This file was deleted.

MCPForUnity/Runtime/Helpers/UnityObjectIdCompatExtensions.cs.meta

Lines changed: 0 additions & 2 deletions
This file was deleted.

MCPForUnity/Runtime/Serialization/UnityTypeConverters.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -414,11 +414,7 @@ public override UnityEngine.Object ReadJson(JsonReader reader, Type objectType,
414414
if (jo.TryGetValue("instanceID", out JToken idToken) && idToken.Type == JTokenType.Integer)
415415
{
416416
int instanceId = idToken.ToObject<int>();
417-
#if UNITY_6000_3_OR_NEWER
418-
UnityEngine.Object obj = UnityEditor.EditorUtility.EntityIdToObject(instanceId);
419-
#else
420-
UnityEngine.Object obj = UnityEditor.EditorUtility.InstanceIDToObject(instanceId);
421-
#endif
417+
UnityEngine.Object obj = UnityObjectIdCompat.InstanceIDToObjectCompat(instanceId);
422418
if (obj != null)
423419
{
424420
// Direct type match

0 commit comments

Comments
 (0)