Skip to content

Commit 3503378

Browse files
authored
refactor: use Tommy TOML library directly for config file manipulation (#328)
Before discovering the Tommy library I tried to parse TOML files directly. Some of the methods lingered from that time, which were messing up configuration on Windows. Now we use Tommy more, it should work. Also consolidated some code with fresh eyes Closes #311
1 parent dbdaa76 commit 3503378

File tree

2 files changed

+69
-102
lines changed

2 files changed

+69
-102
lines changed

MCPForUnity/Editor/Helpers/CodexConfigHelper.cs

Lines changed: 68 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5-
using System.Text;
6-
using System.Text.RegularExpressions;
75
using MCPForUnity.External.Tommy;
8-
using Newtonsoft.Json;
96

107
namespace MCPForUnity.Editor.Helpers
118
{
@@ -42,108 +39,107 @@ public static bool IsCodexConfigured(string pythonDir)
4239

4340
public static string BuildCodexServerBlock(string uvPath, string serverSrc)
4441
{
45-
string argsArray = FormatTomlStringArray(new[] { "run", "--directory", serverSrc, "server.py" });
46-
return $"[mcp_servers.unityMCP]{Environment.NewLine}" +
47-
$"command = \"{EscapeTomlString(uvPath)}\"{Environment.NewLine}" +
48-
$"args = {argsArray}";
42+
var table = new TomlTable();
43+
var mcpServers = new TomlTable();
44+
45+
mcpServers["unityMCP"] = CreateUnityMcpTable(uvPath, serverSrc);
46+
table["mcp_servers"] = mcpServers;
47+
48+
using var writer = new StringWriter();
49+
table.WriteTo(writer);
50+
return writer.ToString();
4951
}
5052

51-
public static string UpsertCodexServerBlock(string existingToml, string newBlock)
53+
public static string UpsertCodexServerBlock(string existingToml, string uvPath, string serverSrc)
5254
{
53-
if (string.IsNullOrWhiteSpace(existingToml))
55+
// Parse existing TOML or create new root table
56+
var root = TryParseToml(existingToml) ?? new TomlTable();
57+
58+
// Ensure mcp_servers table exists
59+
if (!root.TryGetNode("mcp_servers", out var mcpServersNode) || !(mcpServersNode is TomlTable))
5460
{
55-
return newBlock.TrimEnd() + Environment.NewLine;
61+
root["mcp_servers"] = new TomlTable();
5662
}
63+
var mcpServers = root["mcp_servers"] as TomlTable;
5764

58-
StringBuilder sb = new StringBuilder();
59-
using StringReader reader = new StringReader(existingToml);
60-
string line;
61-
bool inTarget = false;
62-
bool replaced = false;
63-
while ((line = reader.ReadLine()) != null)
64-
{
65-
string trimmed = line.Trim();
66-
bool isSection = trimmed.StartsWith("[") && trimmed.EndsWith("]") && !trimmed.StartsWith("[[");
67-
if (isSection)
68-
{
69-
bool isTarget = string.Equals(trimmed, "[mcp_servers.unityMCP]", StringComparison.OrdinalIgnoreCase);
70-
if (isTarget)
71-
{
72-
if (!replaced)
73-
{
74-
if (sb.Length > 0 && sb[^1] != '\n') sb.AppendLine();
75-
sb.AppendLine(newBlock.TrimEnd());
76-
replaced = true;
77-
}
78-
inTarget = true;
79-
continue;
80-
}
65+
// Create or update unityMCP table
66+
mcpServers["unityMCP"] = CreateUnityMcpTable(uvPath, serverSrc);
8167

82-
if (inTarget)
83-
{
84-
inTarget = false;
85-
}
86-
}
68+
// Serialize back to TOML
69+
using var writer = new StringWriter();
70+
root.WriteTo(writer);
71+
return writer.ToString();
72+
}
8773

88-
if (inTarget)
89-
{
90-
continue;
91-
}
74+
public static bool TryParseCodexServer(string toml, out string command, out string[] args)
75+
{
76+
command = null;
77+
args = null;
9278

93-
sb.AppendLine(line);
79+
var root = TryParseToml(toml);
80+
if (root == null) return false;
81+
82+
if (!TryGetTable(root, "mcp_servers", out var servers)
83+
&& !TryGetTable(root, "mcpServers", out servers))
84+
{
85+
return false;
9486
}
9587

96-
if (!replaced)
88+
if (!TryGetTable(servers, "unityMCP", out var unity))
9789
{
98-
if (sb.Length > 0 && sb[^1] != '\n') sb.AppendLine();
99-
sb.AppendLine(newBlock.TrimEnd());
90+
return false;
10091
}
10192

102-
return sb.ToString().TrimEnd() + Environment.NewLine;
93+
command = GetTomlString(unity, "command");
94+
args = GetTomlStringArray(unity, "args");
95+
96+
return !string.IsNullOrEmpty(command) && args != null;
10397
}
10498

105-
public static bool TryParseCodexServer(string toml, out string command, out string[] args)
99+
/// <summary>
100+
/// Safely parses TOML string, returning null on failure
101+
/// </summary>
102+
private static TomlTable TryParseToml(string toml)
106103
{
107-
command = null;
108-
args = null;
109-
if (string.IsNullOrWhiteSpace(toml)) return false;
104+
if (string.IsNullOrWhiteSpace(toml)) return null;
110105

111106
try
112107
{
113108
using var reader = new StringReader(toml);
114-
TomlTable root = TOML.Parse(reader);
115-
if (root == null) return false;
116-
117-
if (!TryGetTable(root, "mcp_servers", out var servers)
118-
&& !TryGetTable(root, "mcpServers", out servers))
119-
{
120-
return false;
121-
}
122-
123-
if (!TryGetTable(servers, "unityMCP", out var unity))
124-
{
125-
return false;
126-
}
127-
128-
command = GetTomlString(unity, "command");
129-
args = GetTomlStringArray(unity, "args");
130-
131-
return !string.IsNullOrEmpty(command) && args != null;
109+
return TOML.Parse(reader);
132110
}
133111
catch (TomlParseException)
134112
{
135-
return false;
113+
return null;
136114
}
137115
catch (TomlSyntaxException)
138116
{
139-
return false;
117+
return null;
140118
}
141119
catch (FormatException)
142120
{
143-
return false;
121+
return null;
144122
}
145123
}
146124

125+
/// <summary>
126+
/// Creates a TomlTable for the unityMCP server configuration
127+
/// </summary>
128+
private static TomlTable CreateUnityMcpTable(string uvPath, string serverSrc)
129+
{
130+
var unityMCP = new TomlTable();
131+
unityMCP["command"] = new TomlString { Value = uvPath };
132+
133+
var argsArray = new TomlArray();
134+
argsArray.Add(new TomlString { Value = "run" });
135+
argsArray.Add(new TomlString { Value = "--directory" });
136+
argsArray.Add(new TomlString { Value = serverSrc });
137+
argsArray.Add(new TomlString { Value = "server.py" });
138+
unityMCP["args"] = argsArray;
139+
140+
return unityMCP;
141+
}
142+
147143
private static bool TryGetTable(TomlTable parent, string key, out TomlTable table)
148144
{
149145
table = null;
@@ -211,33 +207,5 @@ private static string[] GetTomlStringArray(TomlTable table, string key)
211207

212208
return null;
213209
}
214-
215-
private static string FormatTomlStringArray(IEnumerable<string> values)
216-
{
217-
if (values == null) return "[]";
218-
StringBuilder sb = new StringBuilder();
219-
sb.Append('[');
220-
bool first = true;
221-
foreach (string value in values)
222-
{
223-
if (!first)
224-
{
225-
sb.Append(", ");
226-
}
227-
sb.Append('"').Append(EscapeTomlString(value ?? string.Empty)).Append('"');
228-
first = false;
229-
}
230-
sb.Append(']');
231-
return sb.ToString();
232-
}
233-
234-
private static string EscapeTomlString(string value)
235-
{
236-
if (string.IsNullOrEmpty(value)) return string.Empty;
237-
return value
238-
.Replace("\\", "\\\\")
239-
.Replace("\"", "\\\"");
240-
}
241-
242210
}
243211
}

MCPForUnity/Editor/Helpers/McpConfigurationHelper.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,7 @@ public static string ConfigureCodexClient(string pythonDir, string configPath, M
205205
return "Configured successfully";
206206
}
207207

208-
string codexBlock = CodexConfigHelper.BuildCodexServerBlock(uvPath, serverSrc);
209-
string updatedToml = CodexConfigHelper.UpsertCodexServerBlock(existingToml, codexBlock);
208+
string updatedToml = CodexConfigHelper.UpsertCodexServerBlock(existingToml, uvPath, serverSrc);
210209

211210
McpConfigFileHelper.WriteAtomicFile(configPath, updatedToml);
212211

0 commit comments

Comments
 (0)