forked from CoplayDev/unity-mcp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathConfigJsonBuilder.cs
More file actions
202 lines (172 loc) · 7.25 KB
/
ConfigJsonBuilder.cs
File metadata and controls
202 lines (172 loc) · 7.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Clients.Configurators;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
namespace MCPForUnity.Editor.Helpers
{
public static class ConfigJsonBuilder
{
public static string BuildManualConfigJson(string uvPath, McpClient client)
{
var root = new JObject();
bool isVSCode = client?.IsVsCodeLayout == true;
JObject container = isVSCode ? EnsureObject(root, "servers") : EnsureObject(root, "mcpServers");
var unity = new JObject();
PopulateUnityNode(unity, uvPath, client, isVSCode);
container["unityMCP"] = unity;
return root.ToString(Formatting.Indented);
}
public static JObject ApplyUnityServerToExistingConfig(JObject root, string uvPath, McpClient client)
{
if (root == null) root = new JObject();
bool isVSCode = client?.IsVsCodeLayout == true;
JObject container = isVSCode ? EnsureObject(root, "servers") : EnsureObject(root, "mcpServers");
JObject unity = container["unityMCP"] as JObject ?? new JObject();
PopulateUnityNode(unity, uvPath, client, isVSCode);
container["unityMCP"] = unity;
return root;
}
/// <summary>
/// Centralized builder that applies all caveats consistently.
/// - Sets command/args with uvx and package version
/// - Ensures env exists
/// - Adds transport configuration (HTTP or stdio)
/// - Adds disabled:false for Windsurf/Kiro only when missing
/// </summary>
private static void PopulateUnityNode(JObject unity, string uvPath, McpClient client, bool isVSCode)
{
// Get transport preference (default to HTTP)
bool useHttpTransport = client?.SupportsHttpTransport != false && EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
string httpProperty = string.IsNullOrEmpty(client?.HttpUrlProperty) ? "url" : client.HttpUrlProperty;
var urlPropsToRemove = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "url", "serverUrl" };
urlPropsToRemove.Remove(httpProperty);
if (useHttpTransport)
{
// HTTP mode: Use URL, no command
string httpUrl = HttpEndpointUtility.GetMcpRpcUrl();
unity[httpProperty] = httpUrl;
foreach (var prop in urlPropsToRemove)
{
if (unity[prop] != null) unity.Remove(prop);
}
// Remove command/args if they exist from previous config
if (unity["command"] != null) unity.Remove("command");
if (unity["args"] != null) unity.Remove("args");
if (isVSCode)
{
unity["type"] = "http";
}
}
else
{
// Stdio mode: Use uvx command
var (uvxPath, fromUrl, packageName) = AssetPathUtility.GetUvxCommandParts();
bool devForceRefresh = false;
try { devForceRefresh = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false); } catch { }
var toolArgs = BuildUvxArgs(fromUrl, packageName, devForceRefresh);
if (ShouldUseWindowsCmdShim(client))
{
unity["command"] = ResolveCmdPath();
var cmdArgs = new List<string> { "/c", uvxPath };
cmdArgs.AddRange(toolArgs);
unity["args"] = JArray.FromObject(cmdArgs.ToArray());
}
else
{
unity["command"] = uvxPath;
unity["args"] = JArray.FromObject(toolArgs.ToArray());
}
// Remove url/serverUrl if they exist from previous config
if (unity["url"] != null) unity.Remove("url");
if (unity["serverUrl"] != null) unity.Remove("serverUrl");
if (isVSCode)
{
unity["type"] = "stdio";
}
}
// Remove type for non-VSCode clients
if (!isVSCode && unity["type"] != null)
{
unity.Remove("type");
}
bool requiresEnv = client?.EnsureEnvObject == true;
bool stripEnv = client?.StripEnvWhenNotRequired == true;
if (requiresEnv)
{
if (unity["env"] == null)
{
unity["env"] = new JObject();
}
}
else if (stripEnv && unity["env"] != null)
{
unity.Remove("env");
}
if (client?.DefaultUnityFields != null)
{
foreach (var kvp in client.DefaultUnityFields)
{
if (unity[kvp.Key] == null)
{
unity[kvp.Key] = kvp.Value != null ? JToken.FromObject(kvp.Value) : JValue.CreateNull();
}
}
}
}
private static JObject EnsureObject(JObject parent, string name)
{
if (parent[name] is JObject o) return o;
var created = new JObject();
parent[name] = created;
return created;
}
private static IList<string> BuildUvxArgs(string fromUrl, string packageName, bool devForceRefresh)
{
// Dev mode: force a fresh install/resolution (avoids stale cached builds while iterating).
// `--no-cache` is the key flag; `--refresh` ensures metadata is revalidated.
// Keep ordering consistent with other uvx builders: dev flags first, then --from <url>, then package name.
var args = new List<string>();
if (devForceRefresh)
{
args.Add("--no-cache");
args.Add("--refresh");
}
if (!string.IsNullOrEmpty(fromUrl))
{
args.Add("--from");
args.Add(fromUrl);
}
args.Add(packageName);
args.Add("--transport");
args.Add("stdio");
return args;
}
private static bool ShouldUseWindowsCmdShim(McpClient client)
{
if (client == null)
{
return false;
}
return Application.platform == RuntimePlatform.WindowsEditor &&
string.Equals(client.name, ClaudeDesktopConfigurator.ClientName, StringComparison.OrdinalIgnoreCase);
}
private static string ResolveCmdPath()
{
var comSpec = Environment.GetEnvironmentVariable("ComSpec");
if (!string.IsNullOrEmpty(comSpec) && File.Exists(comSpec))
{
return comSpec;
}
string system32Cmd = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
return File.Exists(system32Cmd) ? system32Cmd : "cmd.exe";
}
}
}