From 350337813b9dc1cf1032ac2a991af669bdce4db9 Mon Sep 17 00:00:00 2001 From: Marcus Sanatan Date: Sat, 18 Oct 2025 13:08:41 -0400 Subject: [PATCH] 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 --- .../Editor/Helpers/CodexConfigHelper.cs | 176 +++++++----------- .../Editor/Helpers/McpConfigurationHelper.cs | 3 +- 2 files changed, 73 insertions(+), 106 deletions(-) diff --git a/MCPForUnity/Editor/Helpers/CodexConfigHelper.cs b/MCPForUnity/Editor/Helpers/CodexConfigHelper.cs index fceab47..d3d77dc 100644 --- a/MCPForUnity/Editor/Helpers/CodexConfigHelper.cs +++ b/MCPForUnity/Editor/Helpers/CodexConfigHelper.cs @@ -2,10 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Text.RegularExpressions; using MCPForUnity.External.Tommy; -using Newtonsoft.Json; namespace MCPForUnity.Editor.Helpers { @@ -42,108 +39,107 @@ namespace MCPForUnity.Editor.Helpers public static string BuildCodexServerBlock(string uvPath, string serverSrc) { - string argsArray = FormatTomlStringArray(new[] { "run", "--directory", serverSrc, "server.py" }); - return $"[mcp_servers.unityMCP]{Environment.NewLine}" + - $"command = \"{EscapeTomlString(uvPath)}\"{Environment.NewLine}" + - $"args = {argsArray}"; + var table = new TomlTable(); + var mcpServers = new TomlTable(); + + mcpServers["unityMCP"] = CreateUnityMcpTable(uvPath, serverSrc); + table["mcp_servers"] = mcpServers; + + using var writer = new StringWriter(); + table.WriteTo(writer); + return writer.ToString(); } - public static string UpsertCodexServerBlock(string existingToml, string newBlock) + public static string UpsertCodexServerBlock(string existingToml, string uvPath, string serverSrc) { - if (string.IsNullOrWhiteSpace(existingToml)) + // Parse existing TOML or create new root table + var root = TryParseToml(existingToml) ?? new TomlTable(); + + // Ensure mcp_servers table exists + if (!root.TryGetNode("mcp_servers", out var mcpServersNode) || !(mcpServersNode is TomlTable)) { - return newBlock.TrimEnd() + Environment.NewLine; + root["mcp_servers"] = new TomlTable(); } + var mcpServers = root["mcp_servers"] as TomlTable; - StringBuilder sb = new StringBuilder(); - using StringReader reader = new StringReader(existingToml); - string line; - bool inTarget = false; - bool replaced = false; - while ((line = reader.ReadLine()) != null) - { - string trimmed = line.Trim(); - bool isSection = trimmed.StartsWith("[") && trimmed.EndsWith("]") && !trimmed.StartsWith("[["); - if (isSection) - { - bool isTarget = string.Equals(trimmed, "[mcp_servers.unityMCP]", StringComparison.OrdinalIgnoreCase); - if (isTarget) - { - if (!replaced) - { - if (sb.Length > 0 && sb[^1] != '\n') sb.AppendLine(); - sb.AppendLine(newBlock.TrimEnd()); - replaced = true; - } - inTarget = true; - continue; - } + // Create or update unityMCP table + mcpServers["unityMCP"] = CreateUnityMcpTable(uvPath, serverSrc); - if (inTarget) - { - inTarget = false; - } - } - - if (inTarget) - { - continue; - } - - sb.AppendLine(line); - } - - if (!replaced) - { - if (sb.Length > 0 && sb[^1] != '\n') sb.AppendLine(); - sb.AppendLine(newBlock.TrimEnd()); - } - - return sb.ToString().TrimEnd() + Environment.NewLine; + // Serialize back to TOML + using var writer = new StringWriter(); + root.WriteTo(writer); + return writer.ToString(); } public static bool TryParseCodexServer(string toml, out string command, out string[] args) { command = null; args = null; - if (string.IsNullOrWhiteSpace(toml)) return false; + + var root = TryParseToml(toml); + if (root == null) return false; + + if (!TryGetTable(root, "mcp_servers", out var servers) + && !TryGetTable(root, "mcpServers", out servers)) + { + return false; + } + + if (!TryGetTable(servers, "unityMCP", out var unity)) + { + return false; + } + + command = GetTomlString(unity, "command"); + args = GetTomlStringArray(unity, "args"); + + return !string.IsNullOrEmpty(command) && args != null; + } + + /// + /// Safely parses TOML string, returning null on failure + /// + private static TomlTable TryParseToml(string toml) + { + if (string.IsNullOrWhiteSpace(toml)) return null; try { using var reader = new StringReader(toml); - TomlTable root = TOML.Parse(reader); - if (root == null) return false; - - if (!TryGetTable(root, "mcp_servers", out var servers) - && !TryGetTable(root, "mcpServers", out servers)) - { - return false; - } - - if (!TryGetTable(servers, "unityMCP", out var unity)) - { - return false; - } - - command = GetTomlString(unity, "command"); - args = GetTomlStringArray(unity, "args"); - - return !string.IsNullOrEmpty(command) && args != null; + return TOML.Parse(reader); } catch (TomlParseException) { - return false; + return null; } catch (TomlSyntaxException) { - return false; + return null; } catch (FormatException) { - return false; + return null; } } + /// + /// Creates a TomlTable for the unityMCP server configuration + /// + private static TomlTable CreateUnityMcpTable(string uvPath, string serverSrc) + { + var unityMCP = new TomlTable(); + unityMCP["command"] = new TomlString { Value = uvPath }; + + var argsArray = new TomlArray(); + argsArray.Add(new TomlString { Value = "run" }); + argsArray.Add(new TomlString { Value = "--directory" }); + argsArray.Add(new TomlString { Value = serverSrc }); + argsArray.Add(new TomlString { Value = "server.py" }); + unityMCP["args"] = argsArray; + + return unityMCP; + } + private static bool TryGetTable(TomlTable parent, string key, out TomlTable table) { table = null; @@ -211,33 +207,5 @@ namespace MCPForUnity.Editor.Helpers return null; } - - private static string FormatTomlStringArray(IEnumerable values) - { - if (values == null) return "[]"; - StringBuilder sb = new StringBuilder(); - sb.Append('['); - bool first = true; - foreach (string value in values) - { - if (!first) - { - sb.Append(", "); - } - sb.Append('"').Append(EscapeTomlString(value ?? string.Empty)).Append('"'); - first = false; - } - sb.Append(']'); - return sb.ToString(); - } - - private static string EscapeTomlString(string value) - { - if (string.IsNullOrEmpty(value)) return string.Empty; - return value - .Replace("\\", "\\\\") - .Replace("\"", "\\\""); - } - } } diff --git a/MCPForUnity/Editor/Helpers/McpConfigurationHelper.cs b/MCPForUnity/Editor/Helpers/McpConfigurationHelper.cs index 8e727ef..d88bdbc 100644 --- a/MCPForUnity/Editor/Helpers/McpConfigurationHelper.cs +++ b/MCPForUnity/Editor/Helpers/McpConfigurationHelper.cs @@ -205,8 +205,7 @@ namespace MCPForUnity.Editor.Helpers return "Configured successfully"; } - string codexBlock = CodexConfigHelper.BuildCodexServerBlock(uvPath, serverSrc); - string updatedToml = CodexConfigHelper.UpsertCodexServerBlock(existingToml, codexBlock); + string updatedToml = CodexConfigHelper.UpsertCodexServerBlock(existingToml, uvPath, serverSrc); McpConfigFileHelper.WriteAtomicFile(configPath, updatedToml);