Skip to content

Conversation

@msanatan
Copy link
Contributor

@msanatan msanatan commented Sep 26, 2025

Closes #286, note that Codex uses TOML instead of JSON

Screenshot 2025-09-26 at 12 01 33 AM

Summary by CodeRabbit

  • New Features

    • First-class Codex MCP client: added to client list with automatic detection and configuration; Kiro reordered.
  • Improvements

    • Safer configuration updates via atomic writes.
    • Improved server path resolution and cross-platform path comparisons.
    • Manual configuration UI now shows Codex-specific TOML guidance and CLI instructions.
  • Tests

    • New unit tests validating Codex TOML parsing and related flows.

@msanatan msanatan self-assigned this Sep 26, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds Codex support and related plumbing: new Codex client entry and enum values, Codex TOML helpers, a TOML parser, centralized config/server resolution and atomic file-write utilities, editor window integration and tests, plus accompanying Unity .meta files.

Changes

Cohort / File(s) Summary
Clients list update
UnityMcpBridge/Editor/Data/McpClients.cs
Adds a "Codex CLI" McpClient entry with platform config paths pointing to ~/.codex/config.toml, mcpType = Codex, and default configStatus = "Not Configured".
Codex integration helpers
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs, UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs.meta
New public static CodexConfigHelper: TOML-based detection, parse/build/upsert of [mcp_servers.unityMCP] blocks, TryParseCodexServer, and small TOML string/array helpers; meta added.
MCP config & I/O helpers
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs, .../McpConfigFileHelper.cs.meta
New public static McpConfigFileHelper: arg extraction, cross-platform path comparison, server directory/source resolution (including macOS/Windows specifics), and an atomic file-write implementation with tmp/backup/replace logic; meta added.
Editor windows & flows
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs, UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs
Integrates Codex-aware detection/configuration and manual instructions, replaces ad-hoc writes with McpConfigFileHelper.WriteAtomicFile, centralizes server resolution and config checks, and adds ConfigureCodexClient flow.
Models / Enums
UnityMcpBridge/Editor/Models/McpTypes.cs
Adds Codex and reorders/adds Kiro in McpTypes, changing enum ordering.
TOML parser library
UnityMcpBridge/Editor/External/Tommy.cs, UnityMcpBridge/Editor/External/Tommy.cs.meta
Adds a full TOML model & parser (TomlNode types, parser, exceptions, Parse/Write API) used by Codex helpers; meta added.
Tests & test metadata
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs, .../CodexConfigHelperTests.cs.meta, .../Helpers.meta, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/WriteToConfigTests.cs
Adds NUnit tests for TryParseCodexServer covering multiple TOML formats; adjusts test setup/teardown to disable/clean EditorPrefs auto-registration; includes Unity .meta files.
Misc Unity meta
UnityMcpBridge/Editor/External.meta
Adds folder asset metadata for Editor/External.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Editor as MCPForUnityEditorWindow
  participant Codex as CodexConfigHelper
  participant Cfg as McpConfigFileHelper
  participant FS as Filesystem

  User->>Editor: Open / Trigger setup
  Editor->>Codex: IsCodexConfigured(pythonDir)
  alt codex configured
    Codex-->>Editor: true
    Editor-->>User: skip configuration
  else not configured
    Codex-->>Editor: false
    Editor->>Cfg: ResolveServerDirectory(pythonDir, existingArgs)
    Cfg-->>Editor: serverSrc
    Editor->>Codex: BuildCodexServerBlock(uvPath, serverSrc)
    Codex-->>Editor: tomlBlock
    Editor->>Codex: UpsertCodexServerBlock(existingToml, tomlBlock)
    Codex-->>Editor: updatedToml
    Editor->>Cfg: WriteAtomicFile(configPath, updatedToml)
    Cfg->>FS: tmp/write/backup/replace
    FS-->>Cfg: OK / Error
    Cfg-->>Editor: OK / throw
    Editor-->>User: configured / failed
  end
Loading
sequenceDiagram
  autonumber
  participant Editor
  participant Cfg as McpConfigFileHelper
  participant FS as Filesystem

  Editor->>Cfg: WriteAtomicFile(path, contents)
  note right of Cfg: create temp → write → backup original → replace → cleanup
  Cfg->>FS: perform filesystem ops
  alt success
    FS-->>Cfg: OK
    Cfg-->>Editor: OK
  else failure
    FS-->>Cfg: Error
    Cfg-->>Editor: throw (backup preserved)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • dsarno
  • Scriptwonder

Poem

I nibble TOML beneath the moon,
I hop and parse the config soon.
Paths trimmed neat and writes held tight,
Codex wakes to morning light.
Thump! Unity hums — configs right. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The inclusion of a large external TOML parsing library in Tommy.cs introduces functionality beyond the stated objective of adding Codex autoconfig support and is not referenced by the new CodexConfigHelper, making it unrelated to the linked issue’s requirements. Please move the TOML parser implementation to its own dedicated PR or provide justification for its inclusion and demonstrate its use in the Codex configuration flow.
Docstring Coverage ⚠️ Warning Docstring coverage is 10.78% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Add Codex to autoconfig options” succinctly and accurately describes the primary change of enabling Codex integration into the autoconfig flow without extraneous details or ambiguity.
Linked Issues Check ✅ Passed The pull request fully implements the objective of issue #286 by adding Codex support to the autoconfig pipeline, including enum entries, client registration, configuration helpers, UI integration, and tests for parsing TOML-based Codex settings.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92bbe38 and 68ef6cc.

📒 Files selected for processing (1)
  • UnityMcpBridge/Editor/External/Tommy.cs (1 hunks)
🔇 Additional comments (3)
UnityMcpBridge/Editor/External/Tommy.cs (3)

213-216: Fix sign placement when emitting non-decimal integers

ToInlineToml currently emits negatives as 0x-a, which violates TOML (the sign must precede the base prefix, e.g. -0xA). This produces invalid output for every negative hex/octal/binary value. Please emit the sign before the base prefix.

-        public override string ToInlineToml() =>
-            IntegerBase != Base.Decimal
-                ? $"0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{Convert.ToString(Value, (int) IntegerBase)}"
-                : Value.ToString(CultureInfo.InvariantCulture);
+        public override string ToInlineToml()
+        {
+            if (IntegerBase == Base.Decimal)
+                return Value.ToString(CultureInfo.InvariantCulture);
+
+            var raw = Convert.ToString(Value, (int) IntegerBase);
+            var sign = raw.StartsWith("-", StringComparison.Ordinal) ? "-" : "";
+            var digits = sign.Length == 1 ? raw.Substring(1) : raw;
+            return $"{sign}0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{digits}";
+        }

1144-1148: Parse the sign for based integers

The parser still discards the leading sign for 0x/0o/0b literals, so valid TOML like -0xA becomes a positive value (or fails). Please carry the sign through when converting.

-                var v when TomlSyntax.IsIntegerWithBase(v, out var numberBase) => new TomlInteger
-                {
-                    Value = Convert.ToInt64(value.Substring(2).RemoveAll(TomlSyntax.INT_NUMBER_SEPARATOR), numberBase),
-                    IntegerBase = (TomlInteger.Base) numberBase
-                },
+                var v when TomlSyntax.IsIntegerWithBase(v, out var numberBase) => new TomlInteger
+                {
+                    Value =
+                        (value.Length > 0 && value[0] == '-' ? -1 : 1) *
+                        Convert.ToInt64(
+                            value.Substring(value.Length > 0 && (value[0] == '+' || value[0] == '-') ? 3 : 2)
+                                 .RemoveAll(TomlSyntax.INT_NUMBER_SEPARATOR),
+                            numberBase),
+                    IntegerBase = (TomlInteger.Base) numberBase
+                },

1871-1873: Accept signed based integers in the regex

TomlSyntax.BasedIntegerPattern still rejects -0xA/+0b101. TOML permits an optional sign, so parsing those literals currently fails. Please allow an optional leading +/-.

-        public static readonly Regex BasedIntegerPattern =
-            new(@"^0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+        public static readonly Regex BasedIntegerPattern =
+            new(@"^(\+|-)?0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs (1)

234-240: Align copy button label with TOML instructions

On Line 236 we tell Codex users to paste TOML, yet the button still says “Copy JSON”. Update the label so the UI stays consistent.

             EditorGUILayout.BeginHorizontal();
             GUILayout.FlexibleSpace();
 
+            string copyButtonLabel = mcpClient?.mcpType == McpTypes.Codex ? "Copy TOML" : "Copy JSON";
             if (
                 GUILayout.Button(
-                    "Copy JSON",
+                    copyButtonLabel,
                     copyButtonStyle,
                     GUILayout.Height(25),
                     GUILayout.Width(100)
                 )
             )
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (1)

1716-1776: Critical: Don’t write JSON into Codex TOML on auto-rewrite

When Codex config exists but points to a different directory, the code calls WriteToConfig(...), which writes JSON. For Codex, you must update TOML via ConfigureCodexClient(...).

Apply:

-                            string rewriteResult = WriteToConfig(pythonDir, configPath, mcpClient);
+                            string rewriteResult = (mcpClient.mcpType == McpTypes.Codex)
+                                ? ConfigureCodexClient(pythonDir, configPath, mcpClient)
+                                : WriteToConfig(pythonDir, configPath, mcpClient);
🧹 Nitpick comments (8)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (4)

16-27: Handle --directory=PATH form as well

Currently only --directory <PATH> is supported. Add support for the --directory=<PATH> style to be robust across clients.

Apply:

-        public static string ExtractDirectoryArg(string[] args)
+        public static string ExtractDirectoryArg(string[] args)
         {
             if (args == null) return null;
-            for (int i = 0; i < args.Length - 1; i++)
+            for (int i = 0; i < args.Length; i++)
             {
-                if (string.Equals(args[i], "--directory", StringComparison.OrdinalIgnoreCase))
-                {
-                    return args[i + 1];
-                }
+                var cur = args[i] ?? string.Empty;
+                if (string.Equals(cur, "--directory", StringComparison.OrdinalIgnoreCase) && i < args.Length - 1)
+                    return args[i + 1];
+                if (cur.StartsWith("--directory=", StringComparison.OrdinalIgnoreCase))
+                    return cur.Substring("--directory=".Length);
             }
             return null;
         }

29-46: Normalize trailing separators for stable path equality

Trailing slashes/backslashes can cause false negatives. Trim them before comparing.

-                string na = Path.GetFullPath(a.Trim());
-                string nb = Path.GetFullPath(b.Trim());
+                string na = Path.GetFullPath(a.Trim())
+                    .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+                string nb = Path.GetFullPath(b.Trim())
+                    .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

89-97: Avoid Unity PackageCache path on all OS, not just Windows

The PackageCache path is ephemeral on macOS/Linux too. Generalize the check.

-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
-                && !string.IsNullOrEmpty(serverSrc)
-                && serverSrc.IndexOf(@"\Library\PackageCache\", StringComparison.OrdinalIgnoreCase) >= 0
-                && !EditorPrefs.GetBool("MCPForUnity.UseEmbeddedServer", false))
-            {
-                serverSrc = ServerInstaller.GetServerPath();
-            }
+            string normSrc = (serverSrc ?? string.Empty).Replace('/', '\\');
+            if (!string.IsNullOrEmpty(normSrc)
+                && normSrc.IndexOf(@"\Library\PackageCache\", StringComparison.OrdinalIgnoreCase) >= 0
+                && !EditorPrefs.GetBool("MCPForUnity.UseEmbeddedServer", false))
+            {
+                serverSrc = ServerInstaller.GetServerPath();
+            }

100-107: Create parent directory before atomic write

Prevents failures when the config directory doesn’t exist.

         public static void WriteAtomicFile(string path, string contents)
         {
             string tmp = path + ".tmp";
             string backup = path + ".backup";
             bool writeDone = false;
             try
             {
+                var dir = Path.GetDirectoryName(path);
+                if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
                 File.WriteAllText(tmp, contents, new UTF8Encoding(false));
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (3)

1152-1198: Remove duplicate, unused WriteAtomicFile (use shared helper instead)

This private copy is now redundant; the class already uses McpConfigFileHelper.WriteAtomicFile. Remove to avoid drift.

-        private static void WriteAtomicFile(string path, string contents)
-        {
-            string tmp = path + ".tmp";
-            string backup = path + ".backup";
-            bool writeDone = false;
-            try
-            {
-                File.WriteAllText(tmp, contents, new UTF8Encoding(false));
-                try
-                {
-                    File.Replace(tmp, path, backup);
-                    writeDone = true;
-                }
-                catch (FileNotFoundException)
-                {
-                    File.Move(tmp, path);
-                    writeDone = true;
-                }
-                catch (PlatformNotSupportedException)
-                {
-                    if (File.Exists(path))
-                    {
-                        try { if (File.Exists(backup)) File.Delete(backup); } catch { }
-                        File.Move(path, backup);
-                    }
-                    File.Move(tmp, path);
-                    writeDone = true;
-                }
-            }
-            catch (Exception ex)
-            {
-                try
-                {
-                    if (!writeDone && File.Exists(backup))
-                    {
-                        try { File.Copy(backup, path, true); } catch { }
-                    }
-                }
-                catch { }
-                throw new Exception($"Failed to write config file '{path}': {ex.Message}", ex);
-            }
-            finally
-            {
-                try { if (File.Exists(tmp)) File.Delete(tmp); } catch { }
-                try { if (writeDone && File.Exists(backup)) File.Delete(backup); } catch { }
-            }
-        }
+

706-733: Simplify Cursor config path resolution

Both branches build the same path. Collapse into one assignment.

-            try
-            {
-                string configPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
-                    ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), 
-                        ".cursor", "mcp.json")
-                    : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), 
-                        ".cursor", "mcp.json");
+            try
+            {
+                string configPath = Path.Combine(
+                    Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+                    ".cursor", "mcp.json");

1324-1339: Improve Codex manual block with existing args (if present)

Reuse existing Codex args when building the manual TOML so the suggested directory matches current config.

-            string manualConfig = mcpClient?.mcpType == McpTypes.Codex
-                ? CodexConfigHelper.BuildCodexServerBlock(uvPathForManual, McpConfigFileHelper.ResolveServerDirectory(pythonDir, null)).TrimEnd() + Environment.NewLine
-                : ConfigJsonBuilder.BuildManualConfigJson(uvPathForManual, pythonDir, mcpClient);
+            string manualConfig;
+            if (mcpClient?.mcpType == McpTypes.Codex)
+            {
+                string[] existingArgs = null;
+                try
+                {
+                    if (File.Exists(configPath))
+                    {
+                        var toml = File.ReadAllText(configPath);
+                        if (CodexConfigHelper.TryParseCodexServer(toml, out _, out var parsed))
+                            existingArgs = parsed;
+                    }
+                }
+                catch { }
+                string serverDir = McpConfigFileHelper.ResolveServerDirectory(pythonDir, existingArgs);
+                manualConfig = CodexConfigHelper.BuildCodexServerBlock(uvPathForManual, serverDir).TrimEnd() + Environment.NewLine;
+            }
+            else
+            {
+                manualConfig = ConfigJsonBuilder.BuildManualConfigJson(uvPathForManual, pythonDir, mcpClient);
+            }
UnityMcpBridge/Editor/Data/McpClients.cs (1)

162-183: Clarify Codex listing number

The comment label jumps back to “// 4)” even though earlier entries already use that number. Consider renumbering this and the nearby “// 3) Kiro” comment (or drop the numbering altogether) so the list stays easy to follow.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b57a3b8 and cdbf5be.

📒 Files selected for processing (8)
  • UnityMcpBridge/Editor/Data/McpClients.cs (1 hunks)
  • UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1 hunks)
  • UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs.meta (1 hunks)
  • UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (1 hunks)
  • UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs.meta (1 hunks)
  • UnityMcpBridge/Editor/Models/McpTypes.cs (1 hunks)
  • UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (15 hunks)
  • UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-04T01:01:11.927Z
Learnt from: dsarno
PR: CoplayDev/unity-mcp#260
File: UnityMcpBridge/UnityMcpServer~/src/server_version.txt:1-1
Timestamp: 2025-09-04T01:01:11.927Z
Learning: The UnityMcpBridge project is not maintaining changelogs yet, so don't suggest adding changelog entries for version bumps.

Applied to files:

  • UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs.meta
🧬 Code graph analysis (3)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (4)
UnityMcpBridge/Editor/Tools/MenuItems/MenuItemsReader.cs (1)
  • Exists (78-94)
UnityMcpBridge/Editor/Helpers/ServerInstaller.cs (3)
  • ServerInstaller (12-743)
  • GetServerPath (112-115)
  • EnsureServerInstalled (22-110)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (1)
  • WriteAtomicFile (1152-1198)
UnityMcpBridge/Editor/Helpers/ServerPathResolver.cs (2)
  • ServerPathResolver (8-148)
  • TryFindEmbeddedServerSource (15-100)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (2)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (5)
  • CodexConfigHelper (14-274)
  • IsCodexConfigured (16-38)
  • BuildCodexServerBlock (40-46)
  • TryParseCodexServer (102-146)
  • UpsertCodexServerBlock (48-100)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (6)
  • McpConfigFileHelper (14-185)
  • ExtractDirectoryArg (16-27)
  • PathsEqual (29-46)
  • WriteAtomicFile (100-150)
  • ResolveServerDirectory (52-98)
  • ResolveServerSource (152-184)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (3)
  • McpConfigFileHelper (14-185)
  • ExtractDirectoryArg (16-27)
  • PathsEqual (29-46)
🔇 Additional comments (4)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (2)

571-576: LGTM: Per‑client “already configured” checks now include Codex

Good use of CodexConfigHelper.IsCodexConfigured(pythonDir) alongside existing checks. This avoids redundant rewrites.

Also applies to: 589-593, 669-674, 687-691


1276-1301: LGTM: Centralized resolution and atomic writes

Using McpConfigFileHelper.ResolveServerDirectory(...) and McpConfigFileHelper.WriteAtomicFile(...) improves consistency and safety.

Also applies to: 1556-1575

UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs.meta (1)

1-11: Meta asset looks good

Standard Unity .meta stub with default settings—no issues spotted.

UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs.meta (1)

1-11: Meta asset looks good

Matches the usual Unity defaults; nothing to change.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cdbf5be and 345c06a.

📒 Files selected for processing (4)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers.meta (1 hunks)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1 hunks)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs.meta (1 hunks)
  • UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs.meta
🧰 Additional context used
🧬 Code graph analysis (2)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (2)
  • CodexConfigHelper (15-372)
  • TryParseCodexServer (103-148)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (3)
  • McpConfigFileHelper (14-185)
  • ExtractDirectoryArg (16-27)
  • PathsEqual (29-46)
🔇 Additional comments (1)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1)

8-67: Solid coverage for Codex TOML parsing scenarios.

Great to see the parser exercised across single-line, multi-line, and comment-heavy cases; this gives confidence the helper can tolerate the real Codex configs we’ve seen in the wild.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1)

47-67: Add a test for single-quoted arrays.

ParseTomlStringArray in CodexConfigHelper contains bespoke fallback logic to handle TOML arrays written with single quotes. None of the current tests exercise that branch, so a regression there would slip through. Please add a fifth test (e.g., args = ['run', '--directory', '/abs/path', 'server.py']) to make sure the single-quote path stays green. Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 345c06a and a7e6822.

📒 Files selected for processing (2)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1 hunks)
  • UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (2)
  • CodexConfigHelper (15-373)
  • TryParseCodexServer (103-149)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (3)
  • McpConfigFileHelper (14-185)
  • ExtractDirectoryArg (16-27)
  • PathsEqual (29-46)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7e6822 and 40f22a4.

📒 Files selected for processing (1)
  • UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (3)
  • McpConfigFileHelper (14-185)
  • ExtractDirectoryArg (16-27)
  • PathsEqual (29-46)

It's a single file and OSS, easy to integrate into Unity
… inside literal strings are treated as embedded apostrophes instead of prematurely ending the value
Seeing random JSON in my codex config was pretty annoying
@msanatan
Copy link
Contributor Author

Recent changes still work well

Screenshot 2025-09-26 at 4 43 53 PM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (1)

706-733: Avoid invalid cast when reading Cursor args; use JArray.ToObject<string[]>().

Casting dynamic JArray to IEnumerable can throw; prefer strongly-typed conversion.

Apply this diff:

-                var args = unity.args;
+                var args = unity.args;
                 if (args == null) return false;
-                // Prefer exact extraction of the --directory value and compare normalized paths
-                string[] strArgs = ((System.Collections.Generic.IEnumerable<object>)args)
-                    .Select(x => x?.ToString() ?? string.Empty)
-                    .ToArray();
-                string dir = McpConfigFileHelper.ExtractDirectoryArg(strArgs);
+                // Prefer exact extraction of the --directory value and compare normalized paths
+                var jArr = args as JArray;
+                string[] strArgs = jArr?.ToObject<string[]>();
+                if (strArgs == null) return false;
+                string dir = McpConfigFileHelper.ExtractDirectoryArg(strArgs);
                 if (string.IsNullOrEmpty(dir)) return false;
                 return McpConfigFileHelper.PathsEqual(dir, pythonDir);
🧹 Nitpick comments (11)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/WriteToConfigTests.cs (1)

49-60: Preserve the existing AutoRegister preference during tests.

Hard-setting the key to false and deleting it afterward wipes whatever value the developer had configured (and can flip it back to the default). Cache the existing value in SetUp and restore it in TearDown instead of unconditionally deleting the key.

@@
-        private string _serverSrcDir;
+        private string _serverSrcDir;
+        private bool? _previousAutoRegisterEnabled;
@@
-            // Disable auto-registration to avoid hitting user configs during tests
-            EditorPrefs.SetBool("MCPForUnity.AutoRegisterEnabled", false);
+            const string autoRegisterKey = "MCPForUnity.AutoRegisterEnabled";
+            _previousAutoRegisterEnabled = EditorPrefs.HasKey(autoRegisterKey)
+                ? EditorPrefs.GetBool(autoRegisterKey)
+                : (bool?)null;
+            EditorPrefs.SetBool(autoRegisterKey, false);
@@
-            EditorPrefs.DeleteKey("MCPForUnity.AutoRegisterEnabled");
+            const string autoRegisterKey = "MCPForUnity.AutoRegisterEnabled";
+            if (_previousAutoRegisterEnabled.HasValue)
+            {
+                EditorPrefs.SetBool(autoRegisterKey, _previousAutoRegisterEnabled.Value);
+            }
+            else
+            {
+                EditorPrefs.DeleteKey(autoRegisterKey);
+            }
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (3)

589-593: Make “Connected ✓” reflect any configured client, not just Cursor/Codex/Claude.

Using specific helpers can show false states. Prefer the client statuses already computed.

Apply this diff:

-					lastClientRegisteredOk = anyRegistered
-						|| IsCursorConfigured(pythonDir)
-						|| CodexConfigHelper.IsCodexConfigured(pythonDir)
-						|| IsClaudeConfigured();
+					lastClientRegisteredOk = anyRegistered
+						|| mcpClients.clients.Any(c => c.status == McpStatus.Configured);

687-691: Unify success flag computation with client statuses.

Avoid hard‑coding specific clients; trust statuses post‑configuration.

Apply this diff:

-				lastClientRegisteredOk = anyRegistered
-					|| IsCursorConfigured(pythonDir)
-					|| CodexConfigHelper.IsCodexConfigured(pythonDir)
-					|| IsClaudeConfigured();
+				lastClientRegisteredOk = anyRegistered
+					|| mcpClients.clients.Any(c => c.status == McpStatus.Configured);

1512-1585: Lock preference name is Cursor‑specific; broaden or support both keys.

This gate is now used for Codex too. Consider a generic key while honoring the old one for back‑compat.

Apply this diff:

-			try { if (EditorPrefs.GetBool("MCPForUnity.LockCursorConfig", false)) return "Skipped (locked)"; } catch { }
+			try
+			{
+				bool locked = EditorPrefs.GetBool("MCPForUnity.LockMcpConfig", false)
+					|| EditorPrefs.GetBool("MCPForUnity.LockCursorConfig", false); // back-compat
+				if (locked) return "Skipped (locked)";
+			}
+			catch { }
UnityMcpBridge/Editor/External/Tommy.cs (7)

492-493: Unity/C# compatibility: replace ‘or’ type patterns (C# 9) with C# 8-safe logic

Logical patterns (or) in type patterns require C# 9.

Apply this diff:

-            var hasRealValues = !collapsedItems.All(n => n.Value is TomlTable {IsInline: false} or TomlArray {IsTableArray: true});
+            var hasRealValues = !collapsedItems.All(n =>
+            {
+                var v = n.Value;
+                return (v is TomlTable t && t.IsInline == false) || (v is TomlArray a && a.IsTableArray);
+            });

514-520: Unity/C# compatibility: replace ‘or’ pattern in condition

Same C# 9 logical pattern. Use classic boolean expressions.

Apply this diff:

-                if (collapsedItem.Value is TomlArray {IsTableArray: true} or TomlTable {IsInline: false})
+                if ((collapsedItem.Value is TomlArray a && a.IsTableArray) ||
+                    (collapsedItem.Value is TomlTable t && t.IsInline == false))
                 {

794-801: Unity/C# compatibility: char ‘or’ pattern

Replace with classic || to avoid C# 9 requirement.

-                    if (c is TomlSyntax.COMMENT_SYMBOL or TomlSyntax.NEWLINE_CHARACTER)
+                    if (c == TomlSyntax.COMMENT_SYMBOL || c == TomlSyntax.NEWLINE_CHARACTER)
                     {

1937-1949: Unity/C# compatibility: replace C# 9 relational/logical patterns in helpers

These helpers use C# 9 pattern syntax (is/or/and). Convert to C# 8-friendly expressions.

Apply this diff:

-        public static bool IsQuoted(char c) => c is BASIC_STRING_SYMBOL or LITERAL_STRING_SYMBOL;
+        public static bool IsQuoted(char c) => c == BASIC_STRING_SYMBOL || c == LITERAL_STRING_SYMBOL;
-        public static bool IsWhiteSpace(char c) => c is ' ' or '\t';
+        public static bool IsWhiteSpace(char c) => c == ' ' || c == '\t';
-        public static bool IsNewLine(char c) => c is NEWLINE_CHARACTER or NEWLINE_CARRIAGE_RETURN_CHARACTER;
+        public static bool IsNewLine(char c) => c == NEWLINE_CHARACTER || c == NEWLINE_CARRIAGE_RETURN_CHARACTER;
-        public static bool IsBareKey(char c) =>
-            c is >= 'A' and <= 'Z' or >= 'a' and <= 'z' or >= '0' and <= '9' or '_' or '-';
+        public static bool IsBareKey(char c) =>
+            (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-';
-        public static bool IsValueSeparator(char c) =>
-            c is ITEM_SEPARATOR or ARRAY_END_SYMBOL or INLINE_TABLE_END_SYMBOL;
+        public static bool IsValueSeparator(char c) =>
+            c == ITEM_SEPARATOR || c == ARRAY_END_SYMBOL || c == INLINE_TABLE_END_SYMBOL;

Also applies to: 1958-1960


616-618: TOMLParser.ForceASCII appears unused (redundant with static TOML.ForceASCII)

Parser.ForceASCII is set but never read; escape logic reads TOML.ForceASCII. Either wire the instance setting into escape paths or remove the parser‑level property to avoid confusion.

Also applies to: 1752-1756


421-422: Avoid exception on Delete when node not found

RawTable.First(...) will throw if the node isn’t present. Consider a safe remove.

Example:

-        public override void Delete(TomlNode node) => RawTable.Remove(RawTable.First(kv => kv.Value == node).Key);
+        public override void Delete(TomlNode node)
+        {
+            var kv = RawTable.FirstOrDefault(kv => kv.Value == node);
+            if (!EqualityComparer<string>.Default.Equals(kv.Key, null))
+                RawTable.Remove(kv.Key);
+        }

350-383: Array‑of‑tables comment emission repeats header comment for each table

This writes the same Comment before every [[name]] section. Confirm if that’s intended; if not, emit once or prefer per‑table comments.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40f22a4 and e5a8535.

📒 Files selected for processing (7)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1 hunks)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Windows/WriteToConfigTests.cs (2 hunks)
  • UnityMcpBridge/Editor/External.meta (1 hunks)
  • UnityMcpBridge/Editor/External/Tommy.cs (1 hunks)
  • UnityMcpBridge/Editor/External/Tommy.cs.meta (1 hunks)
  • UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (1 hunks)
  • UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (16 hunks)
✅ Files skipped from review due to trivial changes (2)
  • UnityMcpBridge/Editor/External/Tommy.cs.meta
  • UnityMcpBridge/Editor/External.meta
🚧 Files skipped from review as they are similar to previous changes (2)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs
  • UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs
🧰 Additional context used
🧬 Code graph analysis (1)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (4)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (5)
  • CodexConfigHelper (17-242)
  • IsCodexConfigured (19-41)
  • BuildCodexServerBlock (43-49)
  • TryParseCodexServer (105-145)
  • UpsertCodexServerBlock (51-103)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (6)
  • McpConfigFileHelper (14-185)
  • ExtractDirectoryArg (16-27)
  • PathsEqual (29-46)
  • WriteAtomicFile (100-150)
  • ResolveServerDirectory (52-98)
  • ResolveServerSource (152-184)
UnityMcpBridge/Editor/Helpers/ConfigJsonBuilder.cs (2)
  • ConfigJsonBuilder (7-128)
  • BuildManualConfigJson (9-29)
UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs (2)
  • ManualConfigEditorWindow (9-295)
  • ShowWindow (19-27)
🔇 Additional comments (7)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (7)

1276-1277: LGTM: centralizing server directory resolution.

Using McpConfigFileHelper.ResolveServerDirectory avoids drift and improves cross‑platform behavior.


1301-1301: LGTM: atomic write via shared helper.

Good move to McpConfigFileHelper.WriteAtomicFile.


1324-1339: LGTM: manual config flow handles Codex TOML vs JSON.

Correctly generates TOML block for Codex and JSON for others.


1341-1396: LGTM: robust server source resolution.

Combines installed/dev/embedded resolution with macOS path normalization and debug hints.


1427-1511: LGTM: configure per‑OS path, Codex path, and status update.

Clear split between Codex TOML and JSON clients with proper status handling.


1716-1753: LGTM: client‑specific config parsing, including VSCode and Codex.

Properly handles VSCode’s top‑level servers and Codex TOML via CodexConfigHelper.


1758-1772: LGTM: unified path extraction/compare and auto‑rewrite.

Using McpConfigFileHelper.ExtractDirectoryArg/PathsEqual keeps checks consistent; auto‑rewrite path correctly switches to Codex when needed.

Comment on lines +1 to +10
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace MCPForUnity.External.Tommy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Third‑party code attribution/license

This file appears to embed a TOML parser (“Tommy”). Ensure the repository includes appropriate third‑party attribution and license files per the original project’s terms.

Run to locate licenses/attributions:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Top-level licenses:"
fd -HI --max-depth 2 'LICENSE*' | xargs -I{} sh -c 'echo "== {} =="; head -n 20 "{}"; echo'
echo "Mentions of Tommy/TOML parser:"
rg -n -C2 -i 'tommy|TOML parser|license|third[- ]party|attribution' || true

Length of output: 6022


Add Tommy parser license and attribution
This file embeds the Tommy TOML parser but its original license and copyright notice aren’t included.
Add Tommy’s license (e.g. include its LICENSE file or header notice in UnityMcpBridge/Editor/External/Tommy.cs) and update your top-level LICENSE accordingly.

Comment on lines +185 to +189
public override string ToInlineToml() =>
IntegerBase != Base.Decimal
? $"0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{Convert.ToString(Value, (int) IntegerBase)}"
: Value.ToString(CultureInfo.InvariantCulture);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Correct sign placement for non‑decimal integers (TOML requires sign before 0x/0o/0b)

Negative non-decimal integers currently emit as 0x-a which is invalid. The sign must precede the base prefix, e.g., -0xA.

Apply this diff:

-        public override string ToInlineToml() =>
-            IntegerBase != Base.Decimal
-                ? $"0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{Convert.ToString(Value, (int) IntegerBase)}"
-                : Value.ToString(CultureInfo.InvariantCulture);
+        public override string ToInlineToml()
+        {
+            if (IntegerBase == Base.Decimal)
+                return Value.ToString(CultureInfo.InvariantCulture);
+
+            var raw = Convert.ToString(Value, (int) IntegerBase);
+            var sign = raw.StartsWith("-", StringComparison.Ordinal) ? "-" : "";
+            var digits = sign.Length == 1 ? raw.Substring(1) : raw;
+            return $"{sign}0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{digits}";
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public override string ToInlineToml() =>
IntegerBase != Base.Decimal
? $"0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{Convert.ToString(Value, (int) IntegerBase)}"
: Value.ToString(CultureInfo.InvariantCulture);
}
public override string ToInlineToml()
{
if (IntegerBase == Base.Decimal)
return Value.ToString(CultureInfo.InvariantCulture);
var raw = Convert.ToString(Value, (int) IntegerBase);
var sign = raw.StartsWith("-", StringComparison.Ordinal) ? "-" : "";
var digits = sign.Length == 1 ? raw.Substring(1) : raw;
return $"{sign}0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{digits}";
}
🤖 Prompt for AI Agents
In UnityMcpBridge/Editor/External/Tommy.cs around lines 185 to 189, the current
ToInlineToml emits the sign after the base prefix (e.g., 0x-a) which is invalid
TOML; change the logic so if Value is negative you capture a leading "-" and use
the absolute value for Convert.ToString, then return sign + "0" +
TomlSyntax.BaseIdentifiers[(int)IntegerBase] + convertedAbsoluteValue; keep the
existing decimal branch unchanged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring, we have tests to confirm the TOML parsing and saving works

Comment on lines +1035 to +1040
// Consume the quote character and read the key name
col++;
buffer.Append(ReadQuotedValueSingleLine((char) reader.Read()));
quoted = true;
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix quoted-key parsing: wrong argument passed to ReadQuotedValueSingleLine

The code consumes the opening quote but passes the next character as the quote delimiter. This breaks parsing of quoted keys. Use the actual quote char for the delimiter and pass the first payload char as initial data.

Apply this diff:

-                    // Consume the quote character and read the key name
-                    col++;
-                    buffer.Append(ReadQuotedValueSingleLine((char) reader.Read()));
-                    quoted = true;
-                    continue;
+                    // Consume the quote character and read the key name
+                    ConsumeChar();
+                    var initial = (char) reader.Read();
+                    var key = ReadQuotedValueSingleLine(c, initial);
+                    if (key is null) return AddError("Invalid quoted key.");
+                    buffer.Append(key);
+                    quoted = true;
+                    continue;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Consume the quote character and read the key name
col++;
buffer.Append(ReadQuotedValueSingleLine((char) reader.Read()));
quoted = true;
continue;
}
// Consume the quote character and read the key name
ConsumeChar();
var initial = (char) reader.Read();
var key = ReadQuotedValueSingleLine(c, initial);
if (key is null) return AddError("Invalid quoted key.");
buffer.Append(key);
quoted = true;
continue;
🤖 Prompt for AI Agents
In UnityMcpBridge/Editor/External/Tommy.cs around lines 1035-1040, the code
currently consumes the opening quote then calls ReadQuotedValueSingleLine with
the next character as the quote delimiter; instead, read and store the actual
quote character as the delimiter and pass the first payload character as initial
data to ReadQuotedValueSingleLine. Concretely: after incrementing col, read the
quote char into a variable and then call ReadQuotedValueSingleLine using that
quote char as the delimiter and the next reader.Read() result as the initial
data to append.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring, we have tests to confirm the TOML parsing and saving works

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (3)

585-589: Broaden success condition; avoid hard‑coding specific clients.

lastClientRegisteredOk should reflect any client being configured, not just Cursor/Codex/Claude.

Apply:

-					lastClientRegisteredOk = anyRegistered
-						|| IsCursorConfigured(pythonDir)
-						|| CodexConfigHelper.IsCodexConfigured(pythonDir)
-						|| IsClaudeConfigured();
+					lastClientRegisteredOk = anyRegistered
+						|| mcpClients.clients.Any(c => c.status == McpStatus.Configured);

698-725: Harden Cursor args parsing; prefer JArray.ToObject to dynamic enumeration.

Reduces fragility when Json.NET returns JToken/JArray.

Apply:

-                var args = unity.args;
+                var args = unity.args;
                 if (args == null) return false;
                 // Prefer exact extraction of the --directory value and compare normalized paths
-                string[] strArgs = ((System.Collections.Generic.IEnumerable<object>)args)
-                    .Select(x => x?.ToString() ?? string.Empty)
-                    .ToArray();
-                string dir = McpConfigFileHelper.ExtractDirectoryArg(strArgs);
+                var jarr = args as JArray;
+                string[] strArgs = jarr != null
+                    ? jarr.ToObject<string[]>()
+                    : ((IEnumerable<object>)args).Select(x => x?.ToString() ?? string.Empty).ToArray();
+                string dir = McpConfigFileHelper.ExtractDirectoryArg(strArgs);
                 if (string.IsNullOrEmpty(dir)) return false;
                 return McpConfigFileHelper.PathsEqual(dir, pythonDir);

1456-1529: Codex configuration path is well‑factored.

Parses existing TOML, respects existing uv, reuses ResolveServerDirectory, atomic write, prefs update.

Consider renaming the lock key “MCPForUnity.LockCursorConfig” to a generic “MCPForUnity.LockMcpConfig” in a follow‑up for clarity (touches multiple files).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5a8535 and eff0457.

📒 Files selected for processing (1)
  • UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (15 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (6)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (5)
  • CodexConfigHelper (17-242)
  • IsCodexConfigured (19-41)
  • BuildCodexServerBlock (43-49)
  • TryParseCodexServer (105-145)
  • UpsertCodexServerBlock (51-103)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (6)
  • McpConfigFileHelper (14-185)
  • ExtractDirectoryArg (16-27)
  • PathsEqual (29-46)
  • ResolveServerDirectory (52-98)
  • WriteAtomicFile (100-150)
  • ResolveServerSource (152-184)
UnityMcpBridge/Editor/Models/McpClient.cs (2)
  • McpClient (3-46)
  • SetStatus (33-45)
UnityMcpBridge/Editor/Helpers/ServerInstaller.cs (2)
  • FindUvPath (529-717)
  • ServerInstaller (12-743)
UnityMcpBridge/Editor/Helpers/ConfigJsonBuilder.cs (2)
  • ConfigJsonBuilder (7-128)
  • BuildManualConfigJson (9-29)
UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs (2)
  • ManualConfigEditorWindow (9-295)
  • ShowWindow (19-27)
🔇 Additional comments (9)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (9)

571-574: Per‑client “alreadyConfigured” fix looks good.

Switching to CheckMcpConfiguration + status check resolves the prior gating bug.


665-667: RunSetupNow: per‑client “alreadyConfigured” fix looks good.

Using client.status avoids Cursor‑only gating.


679-683: Mirror the broader success condition here too.

Same improvement as above to avoid special‑casing.

Apply:

-				lastClientRegisteredOk = anyRegistered
-					|| IsCursorConfigured(pythonDir)
-					|| CodexConfigHelper.IsCodexConfigured(pythonDir)
-					|| IsClaudeConfigured();
+				lastClientRegisteredOk = anyRegistered
+					|| mcpClients.clients.Any(c => c.status == McpStatus.Configured);

1245-1245: Nice: centralized atomic writes via McpConfigFileHelper.

Removes duplicated WriteAtomicFile implementation.


1267-1283: Good Codex TOML branch for manual config.

Selecting TOML for Codex and JSON for others is correct.


1285-1288: Consistent server source resolution.

Using McpConfigFileHelper.ResolveServerSource keeps behavior uniform across flows.


1371-1421: ConfigureMcpClient flow looks solid.

  • OS‑specific config path selection
  • Codex routed to ConfigureCodexClient; others to JSON writer
  • Status update on success

1660-1697: Per‑client config detection improvements LGTM.

  • VSCode supports new top‑level servers and legacy schema
  • Codex uses TOML parsing helper
  • Others use standard JSON model

1702-1716: Auto‑rewrite on path drift is appropriate.

Reuses per‑client writers and preserves user experience.

Probably a good habit to keep all 3rd party licenses and copyrights, even if they're also MIT licenses
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
UnityMcpBridge/Editor/External/Tommy.cs (2)

514-520: Replace C# 9 “or” pattern in condition for C# 8 compatibility (if needed).

Same “or” pattern here; use classic type checks.

Apply this diff:

-                if (collapsedItem.Value is TomlArray {IsTableArray: true} or TomlTable {IsInline: false})
+                {
+                var val = collapsedItem.Value;
+                if ((val is TomlArray arr && arr.IsTableArray) || (val is TomlTable tbl && tbl.IsInline == false))
                 {
                     if (!first) tw.WriteLine();
                     first = false;
                     collapsedItem.Value.WriteTo(tw, $"{namePrefix}{key}");
                     continue;
                 }
+                }

1937-1961: Avoid C# 9 relational/“or” patterns in character helpers (if targeting ≤ C# 8).

These helpers use C# 9 patterns. Safer to use classic comparisons for broader Unity compatibility.

Apply this diff:

-        public static bool IsQuoted(char c) => c is BASIC_STRING_SYMBOL or LITERAL_STRING_SYMBOL;
-
-        public static bool IsWhiteSpace(char c) => c is ' ' or '\t';
-
-        public static bool IsNewLine(char c) => c is NEWLINE_CHARACTER or NEWLINE_CARRIAGE_RETURN_CHARACTER;
-
-        public static bool IsLineBreak(char c) => c == NEWLINE_CHARACTER;
-
-        public static bool IsEmptySpace(char c) => IsWhiteSpace(c) || IsNewLine(c);
+        public static bool IsQuoted(char c) => c == BASIC_STRING_SYMBOL || c == LITERAL_STRING_SYMBOL;
+
+        public static bool IsWhiteSpace(char c) => c == ' ' || c == '\t';
+
+        public static bool IsNewLine(char c) => c == NEWLINE_CHARACTER || c == NEWLINE_CARRIAGE_RETURN_CHARACTER;
+
+        public static bool IsLineBreak(char c) => c == NEWLINE_CHARACTER;
+
+        public static bool IsEmptySpace(char c) => IsWhiteSpace(c) || IsNewLine(c);
@@
-        public static bool IsValueSeparator(char c) =>
-            c is ITEM_SEPARATOR or ARRAY_END_SYMBOL or INLINE_TABLE_END_SYMBOL;
+        public static bool IsValueSeparator(char c) =>
+            c == ITEM_SEPARATOR || c == ARRAY_END_SYMBOL || c == INLINE_TABLE_END_SYMBOL;

If the repo targets C# 9+, you can keep the current form. Please confirm with the script in my other comment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eff0457 and 92bbe38.

📒 Files selected for processing (1)
  • UnityMcpBridge/Editor/External/Tommy.cs (1 hunks)
🔇 Additional comments (2)
UnityMcpBridge/Editor/External/Tommy.cs (2)

1-10: Third‑party license/attribution required for embedded TOML parser (Tommy).

This appears to vend a third‑party TOML parser. Ensure the original license and attribution are included in-repo (e.g., LICENSE file in External/, header notice in this file) per upstream terms.

Do you want me to draft the attribution header and add a LICENSE file entry for Tommy?


185-189: Fix non‑decimal integer emission: sign must precede base prefix (-0xA, not 0x-a).

Current ToInlineToml emits invalid TOML for negative hex/oct/bin. Emit the sign before the base prefix.

Apply this diff:

-        public override string ToInlineToml() =>
-            IntegerBase != Base.Decimal
-                ? $"0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{Convert.ToString(Value, (int) IntegerBase)}"
-                : Value.ToString(CultureInfo.InvariantCulture);
+        public override string ToInlineToml()
+        {
+            if (IntegerBase == Base.Decimal)
+                return Value.ToString(CultureInfo.InvariantCulture);
+
+            var raw = Convert.ToString(Value, (int) IntegerBase);
+            var sign = raw.Length > 0 && raw[0] == '-' ? "-" : "";
+            var digits = sign.Length == 1 ? raw.Substring(1) : raw;
+            return $"{sign}0{TomlSyntax.BaseIdentifiers[(int) IntegerBase]}{digits}";
+        }

Comment on lines +492 to +493
var hasRealValues = !collapsedItems.All(n => n.Value is TomlTable {IsInline: false} or TomlArray {IsTableArray: true});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Avoid C# 9 pattern “or” for Unity/C# 8 compatibility (if targeting ≤ C# 8).

This uses C# 9 “or” pattern. If the project targets C# 8 (common on Unity 2021 LTS), rewrite to classic checks.

Apply this diff:

-            var hasRealValues = !collapsedItems.All(n => n.Value is TomlTable {IsInline: false} or TomlArray {IsTableArray: true});
+            var hasRealValues = !collapsedItems.All(n =>
+            {
+                var v = n.Value;
+                return (v is TomlTable tbl && tbl.IsInline == false) || (v is TomlArray arr && arr.IsTableArray);
+            });

To verify language level, run:


🏁 Script executed:

#!/bin/bash
# Inspect Unity version and any LangVersion hints
fd -H ProjectVersion.txt | xargs -I{} sh -c 'echo "== {} =="; cat "{}"'
rg -n -C2 -i 'LangVersion|csc\.rsp|mcs|Roslyn|ApiCompatibilityLevel|ScriptingRuntimeVersion' || true

Length of output: 24064


Replace C# 9 or pattern with classic checks
Unity 2021.3 LTS uses C# ≤8, which doesn’t support pattern combinators.

-            var hasRealValues = !collapsedItems.All(n => n.Value is TomlTable {IsInline: false} or TomlArray {IsTableArray: true});
+            var hasRealValues = !collapsedItems.All(n =>
+            {
+                var v = n.Value;
+                return (v is TomlTable tbl && tbl.IsInline == false) || (v is TomlArray arr && arr.IsTableArray);
+            });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var hasRealValues = !collapsedItems.All(n => n.Value is TomlTable {IsInline: false} or TomlArray {IsTableArray: true});
var hasRealValues = !collapsedItems.All(n =>
{
var v = n.Value;
return (v is TomlTable tbl && tbl.IsInline == false)
|| (v is TomlArray arr && arr.IsTableArray);
});
🤖 Prompt for AI Agents
In UnityMcpBridge/Editor/External/Tommy.cs around lines 492-493, the use of the
C#9 pattern combinator "or" in the lambda (n.Value is TomlTable {IsInline:
false} or TomlArray {IsTableArray: true}) is unsupported in Unity 2021.3 (C#
≤8); replace it with classic checks using explicit || and separate type tests,
e.g. test n.Value is TomlTable tt && tt.IsInline == false OR n.Value is
TomlArray ta && ta.IsTableArray == true, preserving the outer negation and
behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to work OK with Unity 2021, will revisit if we get user errors

Comment on lines +1116 to +1121
var v when TomlSyntax.IsIntegerWithBase(v, out var numberBase) => new TomlInteger
{
Value = Convert.ToInt64(value.Substring(2).RemoveAll(TomlSyntax.INT_NUMBER_SEPARATOR), numberBase),
IntegerBase = (TomlInteger.Base) numberBase
},
var _ => null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Parse sign for non‑decimal integers (current code ignores leading +/-).

When parsing 0x/0o/0b values, the sign is dropped. Honor the sign and adjust the digit offset.

Apply this diff:

-                var v when TomlSyntax.IsIntegerWithBase(v, out var numberBase) => new TomlInteger
-                {
-                    Value = Convert.ToInt64(value.Substring(2).RemoveAll(TomlSyntax.INT_NUMBER_SEPARATOR), numberBase),
-                    IntegerBase = (TomlInteger.Base) numberBase
-                },
+                var v when TomlSyntax.IsIntegerWithBase(v, out var numberBase) => new TomlInteger
+                {
+                    Value =
+                        (value.Length > 0 && value[0] == '-' ? -1 : 1) *
+                        Convert.ToInt64(
+                            value.Substring(value.Length > 0 && (value[0] == '+' || value[0] == '-') ? 3 : 2)
+                                 .RemoveAll(TomlSyntax.INT_NUMBER_SEPARATOR),
+                            numberBase),
+                    IntegerBase = (TomlInteger.Base) numberBase
+                },
🤖 Prompt for AI Agents
In UnityMcpBridge/Editor/External/Tommy.cs around lines 1116 to 1121, the parser
strips the leading sign when converting non‑decimal integer literals (0x/0o/0b);
detect a leading '+' or '-' and set the digit start index to 3 when a sign is
present (2 otherwise), take value.Substring(digitStart).RemoveAll(...), convert
using the detected base, and if the original value had a '-' multiply the
resulting Int64 by -1 before assigning to TomlInteger.Value; keep IntegerBase
assignment the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring, we have tests to confirm the TOML parsing and saving works

Comment on lines +1843 to +1845
public static readonly Regex BasedIntegerPattern =
new(@"^0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Allow optional +/- for non‑decimal integers (parser currently rejects -0xA, +0b101).

TOML permits a leading sign for based integers. Update the regex to accept an optional sign.

Apply this diff:

-        public static readonly Regex BasedIntegerPattern =
-            new(@"^0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+        public static readonly Regex BasedIntegerPattern =
+            new(@"^(\+|-)?0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static readonly Regex BasedIntegerPattern =
new(@"^0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static readonly Regex BasedIntegerPattern =
new(@"^(\+|-)?0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
🤖 Prompt for AI Agents
In UnityMcpBridge/Editor/External/Tommy.cs around lines 1843 to 1845, the
BasedIntegerPattern regex currently rejects signed based integers; update it to
allow an optional leading + or - by adding an optional sign class before the 0
anchor (e.g. change start from ^0 to ^[+-]?0) while keeping the rest of the
pattern and RegexOptions unchanged so values like -0xA and +0b101 are accepted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring, we have tests to confirm the TOML parsing and saving works

@msanatan msanatan merged commit 549ac1e into CoplayDev:main Sep 26, 2025
1 check passed
@msanatan msanatan deleted the autoconfig-codex branch September 26, 2025 22:05
dsarno pushed a commit to dsarno/unity-mcp that referenced this pull request Sep 30, 2025
* feat: add Codex CLI client support with config.toml handling

* feat: add config helpers for managing Codex and MCP server configurations

* feat: add TOML array parsing support for multi-line and trailing comma formats

* fix: handle TOML inline comments in section headers during parsing

* fix: strip TOML comments before processing section headers

* fix: improve JSON parsing to handle escaped single quotes in config strings

* Use Tommy for TOML parsing

It's a single file and OSS, easy to integrate into Unity

* fix: patched Tommy’s literal-string handling so doubled single quotes inside literal strings are treated as embedded apostrophes instead of prematurely ending the value

* Don't overwrite MCP configs while testing

Seeing random JSON in my codex config was pretty annoying

* PR Feedback

* Keep Tommy compatible with Unity 2021

* Re-include Tommy's license

Probably a good habit to keep all 3rd party licenses and copyrights, even if they're also MIT licenses
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Codex to Auto Config

1 participant