Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@ protected static bool IsBetaPackageSource(string packageSource)
return true;

// PyPI prerelease range: >=0.0.0a0 (used for prerelease package builds)
if (packageSource.Contains(">=0.0.0a0", StringComparison.OrdinalIgnoreCase))
if (packageSource.IndexOf(">=0.0.0a0", StringComparison.OrdinalIgnoreCase) >= 0)
return true;

// Git-based beta references
if (packageSource.Contains("@beta", StringComparison.OrdinalIgnoreCase))
if (packageSource.IndexOf("@beta", StringComparison.OrdinalIgnoreCase) >= 0)
return true;

if (packageSource.Contains("-beta", StringComparison.OrdinalIgnoreCase))
if (packageSource.IndexOf("-beta", StringComparison.OrdinalIgnoreCase) >= 0)
return true;

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ protected string BuildAugmentedPath()
if (additions.Length == 0) return null;

// Only return the additions - ExecPath.TryRun will prepend to existing PATH
return string.Join(Path.PathSeparator, additions);
return string.Join(Path.PathSeparator.ToString(), additions);
}

private string[] GetPathAdditions()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ protected string BuildAugmentedPath()
if (additions.Length == 0) return null;

// Only return the additions - ExecPath.TryRun will prepend to existing PATH
return string.Join(Path.PathSeparator, additions);
return string.Join(Path.PathSeparator.ToString(), additions);
}

private string[] GetPathAdditions()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ protected string BuildAugmentedPath()
if (additions.Length == 0) return null;

// Only return the additions - ExecPath.TryRun will prepend to existing PATH
return string.Join(Path.PathSeparator, additions);
return string.Join(Path.PathSeparator.ToString(), additions);
}

private string[] GetPathAdditions()
Expand Down
36 changes: 18 additions & 18 deletions MCPForUnity/Editor/External/Tommy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ internal void WriteTo(TextWriter tw, string name, bool writeSectionName)
if (collapsedItems.Count == 0)
return;

var hasRealValues = !collapsedItems.All(n => n.Value is TomlTable { IsInline: false } or TomlArray { IsTableArray: true });
var hasRealValues = !collapsedItems.All(n => n.Value is TomlTable { IsInline: false } || n.Value is TomlArray { IsTableArray: true });

Comment?.AsComment(tw);

Expand All @@ -539,7 +539,7 @@ internal void WriteTo(TextWriter tw, string name, bool writeSectionName)
foreach (var collapsedItem in collapsedItems)
{
var key = collapsedItem.Key;
if (collapsedItem.Value is TomlArray { IsTableArray: true } or TomlTable { IsInline: false })
if (collapsedItem.Value is TomlArray { IsTableArray: true } || collapsedItem.Value is TomlTable { IsInline: false })
{
if (!first) tw.WriteLine();
first = false;
Expand Down Expand Up @@ -819,7 +819,7 @@ public TomlTable Parse()
if (TomlSyntax.IsWhiteSpace(c) || c == TomlSyntax.NEWLINE_CARRIAGE_RETURN_CHARACTER)
goto consume_character;

if (c is TomlSyntax.COMMENT_SYMBOL or TomlSyntax.NEWLINE_CHARACTER)
if (c == TomlSyntax.COMMENT_SYMBOL || c == TomlSyntax.NEWLINE_CHARACTER)
{
currentState = ParseState.None;
AdvanceLine();
Expand Down Expand Up @@ -1838,13 +1838,13 @@ internal static class TomlSyntax
public const string POS_INF_VALUE = "+inf";
public const string NEG_INF_VALUE = "-inf";

public static bool IsBoolean(string s) => s is TRUE_VALUE or FALSE_VALUE;
public static bool IsBoolean(string s) => s == TRUE_VALUE || s == FALSE_VALUE;

public static bool IsPosInf(string s) => s is INF_VALUE or POS_INF_VALUE;
public static bool IsPosInf(string s) => s == INF_VALUE || s == POS_INF_VALUE;

public static bool IsNegInf(string s) => s == NEG_INF_VALUE;

public static bool IsNaN(string s) => s is NAN_VALUE or POS_NAN_VALUE or NEG_NAN_VALUE;
public static bool IsNaN(string s) => s == NAN_VALUE || s == POS_NAN_VALUE || s == NEG_NAN_VALUE;

public static bool IsInteger(string s) => IntegerPattern.IsMatch(s);

Expand All @@ -1863,25 +1863,25 @@ public static bool IsIntegerWithBase(string s, out int numberBase)
* A pattern to verify the integer value according to the TOML specification.
*/
public static readonly Regex IntegerPattern =
new(@"^(\+|-)?(?!_)(0|(?!0)(_?\d)*)$", RegexOptions.Compiled);
new Regex(@"^(\+|-)?(?!_)(0|(?!0)(_?\d)*)$", RegexOptions.Compiled);

/**
* A pattern to verify a special 0x, 0o and 0b forms of an integer according to the TOML specification.
*/
public static readonly Regex BasedIntegerPattern =
new(@"^0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
new Regex(@"^0(?<base>x|b|o)(?!_)(_?[0-9A-F])*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

/**
* A pattern to verify the float value according to the TOML specification.
*/
public static readonly Regex FloatPattern =
new(@"^(\+|-)?(?!_)(0|(?!0)(_?\d)+)(((e(\+|-)?(?!_)(_?\d)+)?)|(\.(?!_)(_?\d)+(e(\+|-)?(?!_)(_?\d)+)?))$",
new Regex(@"^(\+|-)?(?!_)(0|(?!0)(_?\d)+)(((e(\+|-)?(?!_)(_?\d)+)?)|(\.(?!_)(_?\d)+(e(\+|-)?(?!_)(_?\d)+)?))$",
RegexOptions.Compiled | RegexOptions.IgnoreCase);

/**
* A helper dictionary to map TOML base codes into the radii.
*/
public static readonly Dictionary<string, int> IntegerBases = new()
public static readonly Dictionary<string, int> IntegerBases = new Dictionary<string, int>()
{
["x"] = 16,
["o"] = 8,
Expand All @@ -1891,7 +1891,7 @@ public static bool IsIntegerWithBase(string s, out int numberBase)
/**
* A helper dictionary to map non-decimal bases to their TOML identifiers
*/
public static readonly Dictionary<int, string> BaseIdentifiers = new()
public static readonly Dictionary<int, string> BaseIdentifiers = new Dictionary<int, string>()
{
[2] = "b",
[8] = "o",
Expand Down Expand Up @@ -1962,29 +1962,29 @@ public static bool IsIntegerWithBase(string s, out int numberBase)

public static readonly char[] NewLineCharacters = { NEWLINE_CHARACTER, NEWLINE_CARRIAGE_RETURN_CHARACTER };

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 IsLineBreak(char c) => c == NEWLINE_CHARACTER;

public static bool IsEmptySpace(char c) => IsWhiteSpace(c) || IsNewLine(c);

public static bool IsBareKey(char c) =>
c is >= 'A' and <= 'Z' or >= 'a' and <= 'z' or >= '0' and <= '9' or '_' or '-';
(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-';

public static bool MustBeEscaped(char c, bool allowNewLines = false)
{
var result = c is (>= '\u0000' and <= '\u0008') or '\u000b' or '\u000c' or (>= '\u000e' and <= '\u001f') or '\u007f';
var result = (c >= '\u0000' && c <= '\u0008') || c == '\u000b' || c == '\u000c' || (c >= '\u000e' && c <= '\u001f') || c == '\u007f';
if (!allowNewLines)
result |= c is >= '\u000a' and <= '\u000e';
result |= c >= '\u000a' && c <= '\u000e';
return result;
}

public static bool IsValueSeparator(char c) =>
c is ITEM_SEPARATOR or ARRAY_END_SYMBOL or INLINE_TABLE_END_SYMBOL;
c == ITEM_SEPARATOR || c == ARRAY_END_SYMBOL || c == INLINE_TABLE_END_SYMBOL;

#endregion
}
Expand Down
12 changes: 6 additions & 6 deletions MCPForUnity/Editor/Helpers/AssetPathUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ internal static string ResolveLocalServerPath(string path)
}

// If it looks like a PyPI package reference (no path separators), skip
if (!path.Contains('/') && !path.Contains('\\') && !path.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
if (!path.Contains("/") && !path.Contains("\\") && !path.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
{
return path;
}
Expand Down Expand Up @@ -658,11 +658,11 @@ private static bool IsSemVerPreRelease(string version)

// Common semver prerelease indicators:
// e.g., "9.3.0-beta.1", "9.3.0-alpha", "9.3.0-rc.2", "9.3.0-preview"
return version.Contains("-beta", StringComparison.OrdinalIgnoreCase) ||
version.Contains("-alpha", StringComparison.OrdinalIgnoreCase) ||
version.Contains("-rc", StringComparison.OrdinalIgnoreCase) ||
version.Contains("-preview", StringComparison.OrdinalIgnoreCase) ||
version.Contains("-pre", StringComparison.OrdinalIgnoreCase);
return version.IndexOf("-beta", StringComparison.OrdinalIgnoreCase) >= 0 ||
version.IndexOf("-alpha", StringComparison.OrdinalIgnoreCase) >= 0 ||
version.IndexOf("-rc", StringComparison.OrdinalIgnoreCase) >= 0 ||
version.IndexOf("-preview", StringComparison.OrdinalIgnoreCase) >= 0 ||
version.IndexOf("-pre", StringComparison.OrdinalIgnoreCase) >= 0;
}
}
}
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Helpers/CodexConfigHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ private static void EnsureRmcpClientFeature(TomlTable root)
{
if (root == null) return;

if (!root.TryGetNode("features", out var featuresNode) || featuresNode is not TomlTable features)
if (!root.TryGetNode("features", out var featuresNode) || !(featuresNode is TomlTable features))
{
features = new TomlTable();
root["features"] = features;
Expand Down
4 changes: 4 additions & 0 deletions MCPForUnity/Editor/Helpers/GameObjectLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
using System.Linq;
using Newtonsoft.Json.Linq;
using UnityEditor;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
#else
using UnityEditor.Experimental.SceneManagement;
#endif
using UnityEngine;
using UnityEngine.SceneManagement;
using MCPForUnity.Runtime.Helpers;
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Helpers/HttpEndpointUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ private static string NormalizeBaseUrl(string value, string defaultUrl, bool rem
// Strip trailing "/mcp" (case-insensitive) if provided.
if (trimmed.EndsWith("/mcp", StringComparison.OrdinalIgnoreCase))
{
trimmed = trimmed[..^4];
trimmed = trimmed.Substring(0, trimmed.Length - 4);
}

// For local scope, force 127.0.0.1 over "localhost". Windows getaddrinfo returns ::1
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Helpers/McpConfigurationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static string WriteMcpConfiguration(string configPath, McpClient mcpClien
}
catch { }

JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented };
JsonSerializerSettings jsonSettings = new JsonSerializerSettings() { Formatting = Formatting.Indented };

// Read existing config if it exists
string existingJson = "{}";
Expand Down
5 changes: 3 additions & 2 deletions MCPForUnity/Editor/Helpers/McpLogRecord.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.IO;
using MCPForUnity.Editor.Constants;
using Newtonsoft.Json;
Expand All @@ -15,7 +16,7 @@ internal static class McpLogRecord
private static readonly string ErrorLogPath = Path.Combine(LogDir, "mcpError.log");
private const long MaxLogSizeBytes = 1024 * 1024; // 1 MB
private static bool _sessionStarted;
private static readonly object _logLock = new();
private static readonly object _logLock = new object();
private static volatile bool _isEnabledCached;

[InitializeOnLoadMethod]
Expand Down Expand Up @@ -106,7 +107,7 @@ private static void RotateIfNeeded(string path)

var lines = File.ReadAllLines(path);
var half = lines.Length / 2;
File.WriteAllLines(path, lines[half..]);
File.WriteAllLines(path, new ArraySegment<string>(lines, half, lines.Length - half).ToArray());
}
catch
{
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Helpers/PortManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ private static string ComputeProjectHash(string input)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString()[..8]; // short, sufficient for filenames
return sb.ToString().Substring(0, 8); // short, sufficient for filenames
}
catch
{
Expand Down
70 changes: 70 additions & 0 deletions MCPForUnity/Editor/Helpers/ProcessArgumentListCompat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Diagnostics;

namespace MCPForUnity.Editor.Helpers
{
/// <summary>
/// Unity 2020.3 compatibility shim: ProcessStartInfo.ArgumentList was introduced in
/// .NET Core 2.1 / netstandard2.1. On Unity 2020.3 (netstandard2.0) we emulate it
/// with ProcessStartInfo.Arguments, quoting each argument.
/// </summary>
public static class ProcessArgumentListCompat
{
/// <summary>Append one argument (quoted if it contains whitespace) — replaces ArgumentList.Add.</summary>
public static ProcessStartInfo AddArg(this ProcessStartInfo psi, string arg)
{
if (string.IsNullOrEmpty(psi.Arguments))
{
psi.Arguments = Quote(arg);
}
else
{
psi.Arguments += " " + Quote(arg);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
return psi;
}

private static string Quote(string arg)
{
if (string.IsNullOrEmpty(arg))
{
return "\"\"";
}

// Only quote when necessary; otherwise pass through verbatim so
// backslashes in paths (e.g. C:\Program Files\...) are preserved.
if (arg.IndexOfAny(new[] { ' ', '\t', '"' }) < 0)
{
return arg;
}

// Standard Windows command-line quoting (the algorithm used by
// .NET's ArgumentList / CommandLineToArgvW): wrap in quotes; for every
// run of backslashes, double them only when immediately followed by a
// quote or at the very end of the argument (before the closing quote).
var sb = new System.Text.StringBuilder();
sb.Append('"');
int backslashes = 0;
foreach (char ch in arg)
{
if (ch == '\\')
{
backslashes++;
continue;
}
if (ch == '"')
{
sb.Append('\\', backslashes * 2 + 1);
sb.Append('"');
backslashes = 0;
continue;
}
sb.Append('\\', backslashes);
backslashes = 0;
sb.Append(ch);
}
sb.Append('\\', backslashes * 2);
sb.Append('"');
return sb.ToString();
}
}
}
11 changes: 11 additions & 0 deletions MCPForUnity/Editor/Helpers/ProcessArgumentListCompat.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Helpers/ProjectIdentityUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private static string ComputeProjectName(string dataPath)
projectPath = projectPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (projectPath.EndsWith("Assets", StringComparison.OrdinalIgnoreCase))
{
projectPath = projectPath[..^6].TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
projectPath = projectPath.Substring(0, projectPath.Length - 6).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}

string name = Path.GetFileName(projectPath);
Expand Down
14 changes: 8 additions & 6 deletions MCPForUnity/Editor/Helpers/UnityTypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ namespace MCPForUnity.Editor.Helpers
/// </summary>
public static class UnityTypeResolver
{
private static readonly Dictionary<string, Type> CacheByFqn = new(StringComparer.Ordinal);
private static readonly Dictionary<string, Type> CacheByName = new(StringComparer.Ordinal);
private static readonly Dictionary<string, Type> CacheByFqn = new Dictionary<string, Type>(StringComparer.Ordinal);
private static readonly Dictionary<string, Type> CacheByName = new Dictionary<string, Type>(StringComparer.Ordinal);

/// <summary>
/// Resolves a type by name, with optional base type constraint.
Expand Down Expand Up @@ -150,7 +150,7 @@ private static void Cache(Type t)

private static List<Type> FindCandidates(string query, Type requiredBaseType)
{
bool isShort = !query.Contains('.');
bool isShort = !query.Contains(".");
var loaded = UnityAssembliesCompat.GetLoadedAssemblies();

#if UNITY_EDITOR
Expand All @@ -166,9 +166,11 @@ private static List<Type> FindCandidates(string query, Type requiredBaseType)
var editorAsms = Array.Empty<System.Reflection.Assembly>();
#endif

Func<Type, bool> match = isShort
? (t => t.Name.Equals(query, StringComparison.Ordinal))
: (t => t.FullName?.Equals(query, StringComparison.Ordinal) ?? false);
Func<Type, bool> match;
if (isShort)
match = t => t.Name.Equals(query, StringComparison.Ordinal);
else
match = t => t.FullName?.Equals(query, StringComparison.Ordinal) ?? false;

var fromPlayer = playerAsms.SelectMany(SafeGetTypes)
.Where(t => PassesConstraint(t, requiredBaseType))
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Helpers/VectorParsing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ public static bool ValidateAnimationCurveFormat(JToken valueToken, out string me
for (int i = 0; i < keysArray.Count; i++)
{
var keyToken = keysArray[i];
if (keyToken is not JObject keyObj)
if (!(keyToken is JObject keyObj))
{
message = $"Keyframe at index {i} must be an object with 'time' and 'value'.";
return false;
Expand Down
Loading