Skip to content

Commit c50e583

Browse files
authored
Add distribution settings for Asset Store vs git defaults (#404)
* Add distribution settings for Asset Store vs git defaults Introduce McpDistributionSettings ScriptableObject to configure different defaults for Asset Store and git distributions without code forking. Add skipSetupWindowWhenRemoteDefault flag to bypass setup wizard when shipping with hosted MCP URL. Replace hardcoded localhost:8080 defaults with configurable defaultHttpBaseUrl from distribution settings in HttpEndpointUtility and WebSocketTransportClient. * Improve local address detection in McpDistributionSettings with comprehensive IP range checks Replace simple string-based localhost/127.0.0.1 checks with robust IsLocalAddress method that validates loopback addresses, private IPv4 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16), IPv6 link-local and loopback addresses, and .local hostnames using proper URI parsing and IPAddress validation. * Fix error
1 parent bd620e0 commit c50e583

File tree

6 files changed

+139
-2
lines changed

6 files changed

+139
-2
lines changed

MCPForUnity/Editor/Config.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System;
2+
using System.Net;
3+
using System.Net.Sockets;
4+
using UnityEngine;
5+
6+
namespace MCPForUnity.Editor.Config
7+
{
8+
/// <summary>
9+
/// Distribution controls so we can ship different defaults (Asset Store vs. git) without forking code.
10+
/// </summary>
11+
[CreateAssetMenu(menuName = "MCP/Distribution Settings", fileName = "McpDistributionSettings")]
12+
public class McpDistributionSettings : ScriptableObject
13+
{
14+
[SerializeField] internal string defaultHttpBaseUrl = "http://localhost:8080";
15+
[SerializeField] internal bool skipSetupWindowWhenRemoteDefault = false;
16+
17+
internal bool IsRemoteDefault =>
18+
!string.IsNullOrWhiteSpace(defaultHttpBaseUrl)
19+
&& !IsLocalAddress(defaultHttpBaseUrl);
20+
21+
private static bool IsLocalAddress(string url)
22+
{
23+
if (string.IsNullOrWhiteSpace(url))
24+
{
25+
return true;
26+
}
27+
28+
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
29+
{
30+
return false;
31+
}
32+
33+
string host = uri.Host;
34+
35+
if (string.Equals(host, "localhost", StringComparison.OrdinalIgnoreCase))
36+
{
37+
return true;
38+
}
39+
40+
if (IPAddress.TryParse(host, out var ip))
41+
{
42+
if (IPAddress.IsLoopback(ip))
43+
{
44+
return true;
45+
}
46+
47+
if (ip.AddressFamily == AddressFamily.InterNetwork)
48+
{
49+
var bytes = ip.GetAddressBytes();
50+
// 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16
51+
if (bytes[0] == 10) return true;
52+
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) return true;
53+
if (bytes[0] == 192 && bytes[1] == 168) return true;
54+
if (bytes[0] == 169 && bytes[1] == 254) return true;
55+
}
56+
else if (ip.AddressFamily == AddressFamily.InterNetworkV6)
57+
{
58+
// ::1 loopback or fe80::/10 link-local
59+
if (ip.IsIPv6LinkLocal || ip.Equals(IPAddress.IPv6Loopback))
60+
{
61+
return true;
62+
}
63+
}
64+
65+
return false;
66+
}
67+
68+
// Hostname: treat *.local as local network; otherwise assume remote.
69+
if (host.EndsWith(".local", StringComparison.OrdinalIgnoreCase))
70+
{
71+
return true;
72+
}
73+
74+
return false;
75+
}
76+
}
77+
78+
internal static class McpDistribution
79+
{
80+
private const string ResourcePath = "McpDistributionSettings";
81+
private static McpDistributionSettings _cached;
82+
83+
internal static McpDistributionSettings Settings
84+
{
85+
get
86+
{
87+
if (_cached != null)
88+
{
89+
return _cached;
90+
}
91+
92+
_cached = UnityEngine.Resources.Load<McpDistributionSettings>(ResourcePath);
93+
if (_cached != null)
94+
{
95+
return _cached;
96+
}
97+
98+
// No asset present (git/dev installs) - fall back to baked-in defaults.
99+
_cached = ScriptableObject.CreateInstance<McpDistributionSettings>();
100+
_cached.name = "McpDistributionSettings (Runtime Defaults)";
101+
return _cached;
102+
}
103+
}
104+
}
105+
}

MCPForUnity/Editor/Config/McpDistributionSettings.cs.meta

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

MCPForUnity/Editor/Helpers/HttpEndpointUtility.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using UnityEditor;
33
using MCPForUnity.Editor.Constants;
4+
using MCPForUnity.Editor.Config;
45

56
namespace MCPForUnity.Editor.Helpers
67
{
@@ -12,7 +13,7 @@ namespace MCPForUnity.Editor.Helpers
1213
public static class HttpEndpointUtility
1314
{
1415
private const string PrefKey = EditorPrefKeys.HttpBaseUrl;
15-
private const string DefaultBaseUrl = "http://localhost:8080";
16+
private static string DefaultBaseUrl => McpDistribution.Settings.defaultHttpBaseUrl;
1617

1718
/// <summary>
1819
/// Returns the normalized base URL currently stored in EditorPrefs.

MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Text;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using MCPForUnity.Editor.Config;
910
using MCPForUnity.Editor.Helpers;
1011
using MCPForUnity.Editor.Services.Transport;
1112
using Newtonsoft.Json;
@@ -667,7 +668,7 @@ private static Uri BuildWebSocketUri(string baseUrl)
667668
{
668669
if (string.IsNullOrWhiteSpace(baseUrl))
669670
{
670-
baseUrl = "http://localhost:8080";
671+
baseUrl = McpDistribution.Settings.defaultHttpBaseUrl;
671672
}
672673

673674
if (!Uri.TryCreate(baseUrl, UriKind.Absolute, out var httpUri))

MCPForUnity/Editor/Setup/SetupWindowService.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using MCPForUnity.Editor.Helpers;
55
using MCPForUnity.Editor.Windows;
66
using MCPForUnity.Editor.Constants;
7+
using MCPForUnity.Editor.Config;
78
using UnityEditor;
89
using UnityEngine;
910

@@ -44,6 +45,16 @@ private static void CheckSetupNeeded()
4445
// Check if setup was already completed or dismissed in previous sessions
4546
bool setupCompleted = EditorPrefs.GetBool(SETUP_COMPLETED_KEY, false);
4647
bool setupDismissed = EditorPrefs.GetBool(SETUP_DISMISSED_KEY, false);
48+
bool userOverrodeHttpUrl = EditorPrefs.HasKey(EditorPrefKeys.HttpBaseUrl);
49+
50+
// In Asset Store builds with a remote default URL (and no user override), skip the local setup wizard.
51+
if (!userOverrodeHttpUrl
52+
&& McpDistribution.Settings.skipSetupWindowWhenRemoteDefault
53+
&& McpDistribution.Settings.IsRemoteDefault)
54+
{
55+
McpLog.Info("Skipping Setup Window because this distribution ships with a hosted MCP URL. Open Window/MCP For Unity/Setup Window if you want to configure a local runtime.", always: false);
56+
return;
57+
}
4758

4859
// Only show Setup Window if it hasn't been completed or dismissed before
4960
if (!(setupCompleted || setupDismissed))

0 commit comments

Comments
 (0)