Skip to content

Commit 1b478a0

Browse files
committed
fix: preserve existing environment variables when updating codex server config
1 parent 90ab3e0 commit 1b478a0

File tree

2 files changed

+99
-5
lines changed

2 files changed

+99
-5
lines changed

MCPForUnity/Editor/Helpers/CodexConfigHelper.cs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,19 @@ public static string UpsertCodexServerBlock(string existingToml, string uvPath,
6363
}
6464
var mcpServers = root["mcp_servers"] as TomlTable;
6565

66-
// Create or update unityMCP table
67-
mcpServers["unityMCP"] = CreateUnityMcpTable(uvPath, serverSrc);
66+
// Extract existing env table if unityMCP already exists
67+
TomlTable existingEnvTable = null;
68+
if (mcpServers.TryGetNode("unityMCP", out var existingUnityMcpNode) && existingUnityMcpNode is TomlTable)
69+
{
70+
var existingUnityMcp = existingUnityMcpNode as TomlTable;
71+
if (existingUnityMcp.TryGetNode("env", out var envNode) && envNode is TomlTable)
72+
{
73+
existingEnvTable = envNode as TomlTable;
74+
}
75+
}
76+
77+
// Create or update unityMCP table, preserving existing env table
78+
mcpServers["unityMCP"] = CreateUnityMcpTable(uvPath, serverSrc, existingEnvTable);
6879

6980
// Serialize back to TOML
7081
using var writer = new StringWriter();
@@ -126,7 +137,10 @@ private static TomlTable TryParseToml(string toml)
126137
/// <summary>
127138
/// Creates a TomlTable for the unityMCP server configuration
128139
/// </summary>
129-
private static TomlTable CreateUnityMcpTable(string uvPath, string serverSrc)
140+
/// <param name="uvPath">Path to uv executable</param>
141+
/// <param name="serverSrc">Path to server source directory</param>
142+
/// <param name="existingEnvTable">Optional existing env table to preserve user-defined variables</param>
143+
private static TomlTable CreateUnityMcpTable(string uvPath, string serverSrc, TomlTable existingEnvTable = null)
130144
{
131145
var unityMCP = new TomlTable();
132146
unityMCP["command"] = new TomlString { Value = uvPath };
@@ -142,11 +156,26 @@ private static TomlTable CreateUnityMcpTable(string uvPath, string serverSrc)
142156
var platformService = MCPServiceLocator.Platform;
143157
if (platformService.IsWindows())
144158
{
145-
var envTable = new TomlTable();
159+
// Reuse existing env table (from parameter or unityMCP) to avoid dropping user keys
160+
TomlTable envTable;
161+
if (existingEnvTable != null)
162+
envTable = existingEnvTable;
163+
else if (unityMCP.TryGetNode("env", out var envNode) && envNode is TomlTable existing)
164+
envTable = existing;
165+
else
166+
envTable = new TomlTable();
167+
146168
string systemRoot = platformService.GetSystemRoot();
147-
envTable["SystemRoot"] = new TomlString { Value = systemRoot };
169+
if (!string.IsNullOrEmpty(systemRoot))
170+
envTable["SystemRoot"] = new TomlString { Value = systemRoot };
171+
148172
unityMCP["env"] = envTable;
149173
}
174+
else if (existingEnvTable != null)
175+
{
176+
// Preserve existing env table even on non-Windows platforms
177+
unityMCP["env"] = existingEnvTable;
178+
}
150179

151180
return unityMCP;
152181
}

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,5 +319,70 @@ public void UpsertCodexServerBlock_OnNonWindows_ExcludesEnv()
319319
bool hasEnv = unityMcp.TryGetNode("env", out _);
320320
Assert.IsFalse(hasEnv, "Non-Windows config should not contain env table");
321321
}
322+
323+
[Test]
324+
public void UpsertCodexServerBlock_OnWindows_PreservesExistingEnvVariables()
325+
{
326+
// This test verifies that existing environment variables are preserved when SystemRoot is added
327+
// See: https://github.com/CoplayDev/unity-mcp/issues/315
328+
329+
// Mock Windows platform
330+
MCPServiceLocator.Register<IPlatformService>(new MockPlatformService(isWindows: true, systemRoot: "C:\\Windows"));
331+
332+
// Existing TOML with custom environment variables
333+
string existingToml = string.Join("\n", new[]
334+
{
335+
"[mcp_servers.unityMCP]",
336+
"command = \"uv\"",
337+
"args = [\"run\", \"--directory\", \"C:\\\\old\\\\path\", \"server.py\"]",
338+
"",
339+
"[mcp_servers.unityMCP.env]",
340+
"CUSTOM_VAR = \"custom_value\"",
341+
"ANOTHER_VAR = \"another_value\""
342+
});
343+
344+
string uvPath = "C:\\path\\to\\uv.exe";
345+
string serverSrc = "C:\\path\\to\\server";
346+
347+
string result = CodexConfigHelper.UpsertCodexServerBlock(existingToml, uvPath, serverSrc);
348+
349+
Assert.IsNotNull(result, "UpsertCodexServerBlock should return a valid TOML string");
350+
351+
// Parse the generated TOML to validate structure
352+
TomlTable parsed;
353+
using (var reader = new StringReader(result))
354+
{
355+
parsed = TOML.Parse(reader);
356+
}
357+
358+
// Verify mcp_servers structure
359+
Assert.IsTrue(parsed.TryGetNode("mcp_servers", out var mcpServersNode), "TOML should contain mcp_servers");
360+
var mcpServers = mcpServersNode as TomlTable;
361+
Assert.IsTrue(mcpServers.TryGetNode("unityMCP", out var unityMcpNode), "mcp_servers should contain unityMCP");
362+
var unityMcp = unityMcpNode as TomlTable;
363+
364+
// Verify env table exists
365+
Assert.IsTrue(unityMcp.TryGetNode("env", out var envNode), "Windows config should contain env table");
366+
Assert.IsInstanceOf<TomlTable>(envNode, "env should be a table");
367+
368+
var env = envNode as TomlTable;
369+
370+
// Verify SystemRoot was added
371+
Assert.IsTrue(env.TryGetNode("SystemRoot", out var systemRootNode), "env should contain SystemRoot");
372+
Assert.IsInstanceOf<TomlString>(systemRootNode, "SystemRoot should be a string");
373+
var systemRoot = (systemRootNode as TomlString).Value;
374+
Assert.AreEqual("C:\\Windows", systemRoot, "SystemRoot should be C:\\Windows");
375+
376+
// Verify existing environment variables were preserved
377+
Assert.IsTrue(env.TryGetNode("CUSTOM_VAR", out var customVarNode), "env should preserve CUSTOM_VAR");
378+
Assert.IsInstanceOf<TomlString>(customVarNode, "CUSTOM_VAR should be a string");
379+
var customVar = (customVarNode as TomlString).Value;
380+
Assert.AreEqual("custom_value", customVar, "CUSTOM_VAR should retain its value");
381+
382+
Assert.IsTrue(env.TryGetNode("ANOTHER_VAR", out var anotherVarNode), "env should preserve ANOTHER_VAR");
383+
Assert.IsInstanceOf<TomlString>(anotherVarNode, "ANOTHER_VAR should be a string");
384+
var anotherVar = (anotherVarNode as TomlString).Value;
385+
Assert.AreEqual("another_value", anotherVar, "ANOTHER_VAR should retain its value");
386+
}
322387
}
323388
}

0 commit comments

Comments
 (0)