2025-03-20 19:24:31 +08:00
using System ;
2025-07-30 04:01:17 +08:00
using System.Collections.Generic ;
2025-07-29 01:45:07 +08:00
using System.Diagnostics ;
2025-08-08 08:43:33 +08:00
using System.Security.Cryptography ;
using System.Text ;
using System.Net.Sockets ;
using System.Net ;
2025-04-08 18:14:13 +08:00
using System.IO ;
2025-07-24 11:31:47 +08:00
using System.Linq ;
2025-04-08 18:14:13 +08:00
using System.Runtime.InteropServices ;
using Newtonsoft.Json ;
2025-08-24 18:57:11 +08:00
using Newtonsoft.Json.Linq ;
2025-04-08 18:14:13 +08:00
using UnityEditor ;
using UnityEngine ;
2025-08-21 03:59:49 +08:00
using MCPForUnity.Editor.Data ;
using MCPForUnity.Editor.Helpers ;
using MCPForUnity.Editor.Models ;
2025-03-20 19:24:31 +08:00
2025-08-21 03:59:49 +08:00
namespace MCPForUnity.Editor.Windows
2025-03-20 19:24:31 +08:00
{
2025-08-21 03:59:49 +08:00
public class MCPForUnityEditorWindow : EditorWindow
2025-03-20 19:24:31 +08:00
{
private bool isUnityBridgeRunning = false ;
private Vector2 scrollPosition ;
2025-04-09 19:43:01 +08:00
private string pythonServerInstallationStatus = "Not Installed" ;
private Color pythonServerInstallationStatusColor = Color . red ;
2025-07-29 12:17:36 +08:00
private const int mcpPort = 6500 ; // MCP port (still hardcoded for MCP server)
2025-04-09 19:43:01 +08:00
private readonly McpClients mcpClients = new ( ) ;
2025-08-08 08:43:33 +08:00
private bool autoRegisterEnabled ;
private bool lastClientRegisteredOk ;
private bool lastBridgeVerifiedOk ;
private string pythonDirOverride = null ;
2025-08-09 02:23:45 +08:00
private bool debugLogsEnabled ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
// Script validation settings
private int validationLevelIndex = 1 ; // Default to Standard
private readonly string [ ] validationLevelOptions = new string [ ]
{
"Basic - Only syntax checks" ,
2025-10-01 04:25:33 +08:00
"Standard - Syntax + Unity practices" ,
2025-07-24 11:31:47 +08:00
"Comprehensive - All checks + semantic analysis" ,
"Strict - Full semantic validation (requires Roslyn)"
} ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
// UI state
private int selectedClientIndex = 0 ;
2025-03-20 19:24:31 +08:00
2025-10-04 04:43:40 +08:00
[MenuItem("Window/MCP For Unity")]
2025-03-20 19:24:31 +08:00
public static void ShowWindow ( )
{
2025-10-04 04:43:40 +08:00
GetWindow < MCPForUnityEditorWindow > ( "MCP For Unity" ) ;
2025-03-20 19:24:31 +08:00
}
private void OnEnable ( )
{
2025-04-09 19:43:01 +08:00
UpdatePythonServerInstallationStatus ( ) ;
2025-07-29 12:17:36 +08:00
// Refresh bridge status
2025-08-21 03:59:49 +08:00
isUnityBridgeRunning = MCPForUnityBridge . IsRunning ;
autoRegisterEnabled = EditorPrefs . GetBool ( "MCPForUnity.AutoRegisterEnabled" , true ) ;
debugLogsEnabled = EditorPrefs . GetBool ( "MCPForUnity.DebugLogs" , false ) ;
2025-08-24 13:13:47 +08:00
if ( debugLogsEnabled )
{
LogDebugPrefsState ( ) ;
}
2025-03-20 19:24:31 +08:00
foreach ( McpClient mcpClient in mcpClients . clients )
{
CheckMcpConfiguration ( mcpClient ) ;
}
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
// Load validation level setting
LoadValidationLevelSetting ( ) ;
2025-08-08 08:43:33 +08:00
2025-10-19 08:42:18 +08:00
// Show one-time migration dialog
ShowMigrationDialogIfNeeded ( ) ;
2025-08-12 23:32:51 +08:00
// First-run auto-setup only if Claude CLI is available
if ( autoRegisterEnabled & & ! string . IsNullOrEmpty ( ExecPath . ResolveClaude ( ) ) )
2025-08-08 08:43:33 +08:00
{
AutoFirstRunSetup ( ) ;
}
2025-03-20 19:24:31 +08:00
}
2025-10-01 04:25:33 +08:00
2025-07-29 01:45:07 +08:00
private void OnFocus ( )
{
2025-08-08 06:32:03 +08:00
// Refresh bridge running state on focus in case initialization completed after domain reload
2025-08-21 03:59:49 +08:00
isUnityBridgeRunning = MCPForUnityBridge . IsRunning ;
2025-08-02 11:11:04 +08:00
if ( mcpClients . clients . Count > 0 & & selectedClientIndex < mcpClients . clients . Count )
2025-07-29 01:45:07 +08:00
{
2025-08-02 11:11:04 +08:00
McpClient selectedClient = mcpClients . clients [ selectedClientIndex ] ;
CheckMcpConfiguration ( selectedClient ) ;
2025-07-29 01:45:07 +08:00
}
Repaint ( ) ;
}
2025-03-20 19:24:31 +08:00
private Color GetStatusColor ( McpStatus status )
{
// Return appropriate color based on the status enum
return status switch
{
McpStatus . Configured = > Color . green ,
McpStatus . Running = > Color . green ,
McpStatus . Connected = > Color . green ,
McpStatus . IncorrectPath = > Color . yellow ,
McpStatus . CommunicationError = > Color . yellow ,
McpStatus . NoResponse = > Color . yellow ,
2025-04-08 18:14:13 +08:00
_ = > Color . red , // Default to red for error states or not configured
2025-03-20 19:24:31 +08:00
} ;
}
2025-04-09 19:43:01 +08:00
private void UpdatePythonServerInstallationStatus ( )
{
2025-08-09 05:16:25 +08:00
try
2025-04-09 19:43:01 +08:00
{
2025-08-09 05:16:25 +08:00
string installedPath = ServerInstaller . GetServerPath ( ) ;
bool installedOk = ! string . IsNullOrEmpty ( installedPath ) & & File . Exists ( Path . Combine ( installedPath , "server.py" ) ) ;
if ( installedOk )
{
pythonServerInstallationStatus = "Installed" ;
pythonServerInstallationStatusColor = Color . green ;
return ;
}
// Fall back to embedded/dev source via our existing resolution logic
string embeddedPath = FindPackagePythonDirectory ( ) ;
bool embeddedOk = ! string . IsNullOrEmpty ( embeddedPath ) & & File . Exists ( Path . Combine ( embeddedPath , "server.py" ) ) ;
if ( embeddedOk )
{
pythonServerInstallationStatus = "Installed (Embedded)" ;
pythonServerInstallationStatusColor = Color . green ;
}
else
{
pythonServerInstallationStatus = "Not Installed" ;
pythonServerInstallationStatusColor = Color . red ;
}
2025-04-09 19:43:01 +08:00
}
2025-08-09 05:16:25 +08:00
catch
2025-04-09 19:43:01 +08:00
{
pythonServerInstallationStatus = "Not Installed" ;
pythonServerInstallationStatusColor = Color . red ;
}
}
2025-07-24 11:31:47 +08:00
private void DrawStatusDot ( Rect statusRect , Color statusColor , float size = 12 )
2025-03-20 19:24:31 +08:00
{
2025-07-24 11:31:47 +08:00
float offsetX = ( statusRect . width - size ) / 2 ;
float offsetY = ( statusRect . height - size ) / 2 ;
Rect dotRect = new ( statusRect . x + offsetX , statusRect . y + offsetY , size , size ) ;
Vector3 center = new (
dotRect . x + ( dotRect . width / 2 ) ,
dotRect . y + ( dotRect . height / 2 ) ,
0
) ;
float radius = size / 2 ;
2025-03-20 19:24:31 +08:00
2025-07-24 11:31:47 +08:00
// Draw the main dot
Handles . color = statusColor ;
Handles . DrawSolidDisc ( center , Vector3 . forward , radius ) ;
2025-03-20 19:24:31 +08:00
2025-07-24 11:31:47 +08:00
// Draw the border
Color borderColor = new (
statusColor . r * 0.7f ,
statusColor . g * 0.7f ,
statusColor . b * 0.7f
) ;
Handles . color = borderColor ;
Handles . DrawWireDisc ( center , Vector3 . forward , radius ) ;
}
2025-03-20 19:24:31 +08:00
2025-07-24 11:31:47 +08:00
private void OnGUI ( )
{
scrollPosition = EditorGUILayout . BeginScrollView ( scrollPosition ) ;
2025-10-19 08:42:18 +08:00
// Migration warning banner (non-dismissible)
DrawMigrationWarningBanner ( ) ;
2025-07-24 11:31:47 +08:00
// Header
DrawHeader ( ) ;
2025-10-01 04:25:33 +08:00
2025-08-09 02:23:45 +08:00
// Compute equal column widths for uniform layout
float horizontalSpacing = 2f ;
float outerPadding = 20f ; // approximate padding
// Make columns a bit less wide for a tighter layout
float computed = ( position . width - outerPadding - horizontalSpacing ) / 2f ;
float colWidth = Mathf . Clamp ( computed , 220f , 340f ) ;
// Use fixed heights per row so paired panels match exactly
float topPanelHeight = 190f ;
float bottomPanelHeight = 230f ;
// Top row: Server Status (left) and Unity Bridge (right)
EditorGUILayout . BeginHorizontal ( ) ;
{
EditorGUILayout . BeginVertical ( GUILayout . Width ( colWidth ) , GUILayout . Height ( topPanelHeight ) ) ;
DrawServerStatusSection ( ) ;
EditorGUILayout . EndVertical ( ) ;
EditorGUILayout . Space ( horizontalSpacing ) ;
EditorGUILayout . BeginVertical ( GUILayout . Width ( colWidth ) , GUILayout . Height ( topPanelHeight ) ) ;
DrawBridgeSection ( ) ;
EditorGUILayout . EndVertical ( ) ;
}
EditorGUILayout . EndHorizontal ( ) ;
2025-08-08 08:43:33 +08:00
EditorGUILayout . Space ( 10 ) ;
2025-08-09 02:23:45 +08:00
// Second row: MCP Client Configuration (left) and Script Validation (right)
EditorGUILayout . BeginHorizontal ( ) ;
{
EditorGUILayout . BeginVertical ( GUILayout . Width ( colWidth ) , GUILayout . Height ( bottomPanelHeight ) ) ;
DrawUnifiedClientConfiguration ( ) ;
EditorGUILayout . EndVertical ( ) ;
EditorGUILayout . Space ( horizontalSpacing ) ;
EditorGUILayout . BeginVertical ( GUILayout . Width ( colWidth ) , GUILayout . Height ( bottomPanelHeight ) ) ;
DrawValidationSection ( ) ;
EditorGUILayout . EndVertical ( ) ;
}
EditorGUILayout . EndHorizontal ( ) ;
// Minimal bottom padding
EditorGUILayout . Space ( 2 ) ;
2025-07-24 11:31:47 +08:00
EditorGUILayout . EndScrollView ( ) ;
}
private void DrawHeader ( )
{
EditorGUILayout . Space ( 15 ) ;
Rect titleRect = EditorGUILayout . GetControlRect ( false , 40 ) ;
EditorGUI . DrawRect ( titleRect , new Color ( 0.2f , 0.2f , 0.2f , 0.1f ) ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
GUIStyle titleStyle = new GUIStyle ( EditorStyles . boldLabel )
{
fontSize = 16 ,
alignment = TextAnchor . MiddleLeft
} ;
2025-10-01 04:25:33 +08:00
2025-04-08 18:14:13 +08:00
GUI . Label (
2025-07-24 11:31:47 +08:00
new Rect ( titleRect . x + 15 , titleRect . y + 8 , titleRect . width - 30 , titleRect . height ) ,
2025-10-04 04:43:40 +08:00
"MCP For Unity" ,
2025-07-24 11:31:47 +08:00
titleStyle
2025-04-08 18:14:13 +08:00
) ;
2025-08-09 05:16:25 +08:00
// Place the Show Debug Logs toggle on the same header row, right-aligned
float toggleWidth = 160f ;
Rect toggleRect = new Rect ( titleRect . xMax - toggleWidth - 12f , titleRect . y + 10f , toggleWidth , 20f ) ;
bool newDebug = GUI . Toggle ( toggleRect , debugLogsEnabled , "Show Debug Logs" ) ;
if ( newDebug ! = debugLogsEnabled )
{
debugLogsEnabled = newDebug ;
2025-08-21 03:59:49 +08:00
EditorPrefs . SetBool ( "MCPForUnity.DebugLogs" , debugLogsEnabled ) ;
2025-08-24 13:13:47 +08:00
if ( debugLogsEnabled )
{
LogDebugPrefsState ( ) ;
}
2025-08-09 05:16:25 +08:00
}
2025-07-24 11:31:47 +08:00
EditorGUILayout . Space ( 15 ) ;
}
2025-03-20 19:24:31 +08:00
2025-08-24 13:13:47 +08:00
private void LogDebugPrefsState ( )
{
try
{
string pythonDirOverridePref = SafeGetPrefString ( "MCPForUnity.PythonDirOverride" ) ;
string uvPathPref = SafeGetPrefString ( "MCPForUnity.UvPath" ) ;
string serverSrcPref = SafeGetPrefString ( "MCPForUnity.ServerSrc" ) ;
bool useEmbedded = SafeGetPrefBool ( "MCPForUnity.UseEmbeddedServer" ) ;
// Version-scoped detection key
string embeddedVer = ReadEmbeddedVersionOrFallback ( ) ;
string detectKey = $"MCPForUnity.LegacyDetectLogged:{embeddedVer}" ;
bool detectLogged = SafeGetPrefBool ( detectKey ) ;
// Project-scoped auto-register key
string projectPath = Application . dataPath ? ? string . Empty ;
string autoKey = $"MCPForUnity.AutoRegistered.{ComputeSha1(projectPath)}" ;
bool autoRegistered = SafeGetPrefBool ( autoKey ) ;
MCPForUnity . Editor . Helpers . McpLog . Info (
"MCP Debug Prefs:\n" +
$" DebugLogs: {debugLogsEnabled}\n" +
$" PythonDirOverride: '{pythonDirOverridePref}'\n" +
$" UvPath: '{uvPathPref}'\n" +
$" ServerSrc: '{serverSrcPref}'\n" +
$" UseEmbeddedServer: {useEmbedded}\n" +
$" DetectOnceKey: '{detectKey}' => {detectLogged}\n" +
$" AutoRegisteredKey: '{autoKey}' => {autoRegistered}" ,
always : false
) ;
}
catch ( Exception ex )
{
UnityEngine . Debug . LogWarning ( $"MCP Debug Prefs logging failed: {ex.Message}" ) ;
}
}
private static string SafeGetPrefString ( string key )
{
try { return EditorPrefs . GetString ( key , string . Empty ) ? ? string . Empty ; } catch { return string . Empty ; }
}
private static bool SafeGetPrefBool ( string key )
{
try { return EditorPrefs . GetBool ( key , false ) ; } catch { return false ; }
}
private static string ReadEmbeddedVersionOrFallback ( )
{
try
{
if ( ServerPathResolver . TryFindEmbeddedServerSource ( out var embeddedSrc ) )
{
var p = Path . Combine ( embeddedSrc , "server_version.txt" ) ;
if ( File . Exists ( p ) )
{
var s = File . ReadAllText ( p ) ? . Trim ( ) ;
if ( ! string . IsNullOrEmpty ( s ) ) return s ;
}
}
}
catch { }
return "unknown" ;
}
2025-07-24 11:31:47 +08:00
private void DrawServerStatusSection ( )
{
EditorGUILayout . BeginVertical ( EditorStyles . helpBox ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
GUIStyle sectionTitleStyle = new GUIStyle ( EditorStyles . boldLabel )
{
fontSize = 14
} ;
EditorGUILayout . LabelField ( "Server Status" , sectionTitleStyle ) ;
EditorGUILayout . Space ( 8 ) ;
EditorGUILayout . BeginHorizontal ( ) ;
Rect statusRect = GUILayoutUtility . GetRect ( 0 , 28 , GUILayout . Width ( 24 ) ) ;
DrawStatusDot ( statusRect , pythonServerInstallationStatusColor , 16 ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
GUIStyle statusStyle = new GUIStyle ( EditorStyles . label )
{
fontSize = 12 ,
fontStyle = FontStyle . Bold
} ;
EditorGUILayout . LabelField ( pythonServerInstallationStatus , statusStyle , GUILayout . Height ( 28 ) ) ;
EditorGUILayout . EndHorizontal ( ) ;
2025-03-20 19:24:31 +08:00
2025-07-24 11:31:47 +08:00
EditorGUILayout . Space ( 5 ) ;
2025-10-01 04:25:33 +08:00
2025-07-29 12:17:36 +08:00
EditorGUILayout . BeginHorizontal ( ) ;
2025-08-21 03:59:49 +08:00
bool isAutoMode = MCPForUnityBridge . IsAutoConnectMode ( ) ;
2025-07-29 12:17:36 +08:00
GUIStyle modeStyle = new GUIStyle ( EditorStyles . miniLabel ) { fontSize = 11 } ;
EditorGUILayout . LabelField ( $"Mode: {(isAutoMode ? " Auto " : " Standard ")}" , modeStyle ) ;
2025-08-08 08:43:33 +08:00
GUILayout . FlexibleSpace ( ) ;
2025-07-29 12:17:36 +08:00
EditorGUILayout . EndHorizontal ( ) ;
2025-10-01 04:25:33 +08:00
2025-08-21 03:59:49 +08:00
int currentUnityPort = MCPForUnityBridge . GetCurrentPort ( ) ;
2025-07-24 11:31:47 +08:00
GUIStyle portStyle = new GUIStyle ( EditorStyles . miniLabel )
{
fontSize = 11
} ;
2025-07-29 12:17:36 +08:00
EditorGUILayout . LabelField ( $"Ports: Unity {currentUnityPort}, MCP {mcpPort}" , portStyle ) ;
2025-07-24 11:31:47 +08:00
EditorGUILayout . Space ( 5 ) ;
2025-08-08 08:43:33 +08:00
2025-08-09 05:16:25 +08:00
/// Auto-Setup button below ports
2025-08-09 02:23:45 +08:00
string setupButtonText = ( lastClientRegisteredOk & & lastBridgeVerifiedOk ) ? "Connected ✓" : "Auto-Setup" ;
if ( GUILayout . Button ( setupButtonText , GUILayout . Height ( 24 ) ) )
{
RunSetupNow ( ) ;
}
EditorGUILayout . Space ( 4 ) ;
2025-10-04 06:53:09 +08:00
// Rebuild MCP Server button with tooltip tag
2025-08-09 02:23:45 +08:00
using ( new EditorGUILayout . HorizontalScope ( ) )
{
GUILayout . FlexibleSpace ( ) ;
2025-08-09 05:16:25 +08:00
GUIContent repairLabel = new GUIContent (
2025-10-04 06:53:09 +08:00
"Rebuild MCP Server" ,
"Deletes the installed server and re-copies it from the package. Use this to update the server after making source code changes or if the installation is corrupted."
2025-08-09 05:16:25 +08:00
) ;
if ( GUILayout . Button ( repairLabel , GUILayout . Width ( 160 ) , GUILayout . Height ( 22 ) ) )
2025-08-09 02:23:45 +08:00
{
2025-10-04 06:53:09 +08:00
bool ok = global :: MCPForUnity . Editor . Helpers . ServerInstaller . RebuildMcpServer ( ) ;
2025-08-09 05:16:25 +08:00
if ( ok )
{
2025-10-04 06:53:09 +08:00
EditorUtility . DisplayDialog ( "MCP For Unity" , "Server rebuilt successfully." , "OK" ) ;
2025-08-09 05:16:25 +08:00
UpdatePythonServerInstallationStatus ( ) ;
}
else
{
2025-10-04 06:53:09 +08:00
EditorUtility . DisplayDialog ( "MCP For Unity" , "Rebuild failed. Please check Console for details." , "OK" ) ;
2025-08-09 05:16:25 +08:00
}
2025-08-09 02:23:45 +08:00
}
}
2025-08-09 05:16:25 +08:00
// (Removed descriptive tool tag under the Repair button)
// (Show Debug Logs toggle moved to header)
2025-08-09 02:23:45 +08:00
EditorGUILayout . Space ( 2 ) ;
2025-08-09 05:16:25 +08:00
// Python detection warning with link
if ( ! IsPythonDetected ( ) )
{
GUIStyle warnStyle = new GUIStyle ( EditorStyles . label ) { richText = true , wordWrap = true } ;
EditorGUILayout . LabelField ( "<color=#cc3333><b>Warning:</b></color> No Python installation found." , warnStyle ) ;
using ( new EditorGUILayout . HorizontalScope ( ) )
{
2025-08-21 03:59:49 +08:00
if ( GUILayout . Button ( "Open Install Instructions" , GUILayout . Width ( 200 ) ) )
2025-08-09 05:16:25 +08:00
{
Application . OpenURL ( "https://www.python.org/downloads/" ) ;
}
}
EditorGUILayout . Space ( 4 ) ;
}
2025-08-08 08:43:33 +08:00
// Troubleshooting helpers
if ( pythonServerInstallationStatusColor ! = Color . green )
{
using ( new EditorGUILayout . HorizontalScope ( ) )
{
if ( GUILayout . Button ( "Select server folder…" , GUILayout . Width ( 160 ) ) )
{
string picked = EditorUtility . OpenFolderPanel ( "Select UnityMcpServer/src" , Application . dataPath , "" ) ;
if ( ! string . IsNullOrEmpty ( picked ) & & File . Exists ( Path . Combine ( picked , "server.py" ) ) )
{
pythonDirOverride = picked ;
2025-08-21 03:59:49 +08:00
EditorPrefs . SetString ( "MCPForUnity.PythonDirOverride" , pythonDirOverride ) ;
2025-08-08 08:43:33 +08:00
UpdatePythonServerInstallationStatus ( ) ;
}
else if ( ! string . IsNullOrEmpty ( picked ) )
{
EditorUtility . DisplayDialog ( "Invalid Selection" , "The selected folder does not contain server.py" , "OK" ) ;
}
}
if ( GUILayout . Button ( "Verify again" , GUILayout . Width ( 120 ) ) )
{
UpdatePythonServerInstallationStatus ( ) ;
}
}
}
2025-07-24 11:31:47 +08:00
EditorGUILayout . EndVertical ( ) ;
}
2025-03-20 19:24:31 +08:00
2025-07-24 11:31:47 +08:00
private void DrawBridgeSection ( )
{
EditorGUILayout . BeginVertical ( EditorStyles . helpBox ) ;
2025-10-01 04:25:33 +08:00
2025-08-08 06:32:03 +08:00
// Always reflect the live state each repaint to avoid stale UI after recompiles
2025-08-21 03:59:49 +08:00
isUnityBridgeRunning = MCPForUnityBridge . IsRunning ;
2025-08-08 06:32:03 +08:00
2025-07-24 11:31:47 +08:00
GUIStyle sectionTitleStyle = new GUIStyle ( EditorStyles . boldLabel )
{
fontSize = 14
} ;
EditorGUILayout . LabelField ( "Unity Bridge" , sectionTitleStyle ) ;
EditorGUILayout . Space ( 8 ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
EditorGUILayout . BeginHorizontal ( ) ;
Color bridgeColor = isUnityBridgeRunning ? Color . green : Color . red ;
Rect bridgeStatusRect = GUILayoutUtility . GetRect ( 0 , 28 , GUILayout . Width ( 24 ) ) ;
DrawStatusDot ( bridgeStatusRect , bridgeColor , 16 ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
GUIStyle bridgeStatusStyle = new GUIStyle ( EditorStyles . label )
{
fontSize = 12 ,
fontStyle = FontStyle . Bold
} ;
EditorGUILayout . LabelField ( isUnityBridgeRunning ? "Running" : "Stopped" , bridgeStatusStyle , GUILayout . Height ( 28 ) ) ;
2025-03-20 19:24:31 +08:00
EditorGUILayout . EndHorizontal ( ) ;
EditorGUILayout . Space ( 8 ) ;
2025-07-24 11:31:47 +08:00
if ( GUILayout . Button ( isUnityBridgeRunning ? "Stop Bridge" : "Start Bridge" , GUILayout . Height ( 32 ) ) )
{
ToggleUnityBridge ( ) ;
}
EditorGUILayout . Space ( 5 ) ;
EditorGUILayout . EndVertical ( ) ;
}
2025-03-20 19:24:31 +08:00
2025-07-24 11:31:47 +08:00
private void DrawValidationSection ( )
{
EditorGUILayout . BeginVertical ( EditorStyles . helpBox ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
GUIStyle sectionTitleStyle = new GUIStyle ( EditorStyles . boldLabel )
2025-04-09 21:10:21 +08:00
{
2025-07-24 11:31:47 +08:00
fontSize = 14
2025-04-09 21:10:21 +08:00
} ;
2025-07-24 11:31:47 +08:00
EditorGUILayout . LabelField ( "Script Validation" , sectionTitleStyle ) ;
EditorGUILayout . Space ( 8 ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
EditorGUI . BeginChangeCheck ( ) ;
validationLevelIndex = EditorGUILayout . Popup ( "Validation Level" , validationLevelIndex , validationLevelOptions , GUILayout . Height ( 20 ) ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
SaveValidationLevelSetting ( ) ;
}
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
EditorGUILayout . Space ( 8 ) ;
string description = GetValidationLevelDescription ( validationLevelIndex ) ;
EditorGUILayout . HelpBox ( description , MessageType . Info ) ;
2025-08-09 02:23:45 +08:00
EditorGUILayout . Space ( 4 ) ;
2025-08-09 05:16:25 +08:00
// (Show Debug Logs toggle moved to header)
2025-08-09 02:23:45 +08:00
EditorGUILayout . Space ( 2 ) ;
2025-07-24 11:31:47 +08:00
EditorGUILayout . EndVertical ( ) ;
}
2025-03-20 19:24:31 +08:00
2025-07-24 11:31:47 +08:00
private void DrawUnifiedClientConfiguration ( )
{
EditorGUILayout . BeginVertical ( EditorStyles . helpBox ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
GUIStyle sectionTitleStyle = new GUIStyle ( EditorStyles . boldLabel )
{
fontSize = 14
} ;
EditorGUILayout . LabelField ( "MCP Client Configuration" , sectionTitleStyle ) ;
EditorGUILayout . Space ( 10 ) ;
2025-10-01 04:25:33 +08:00
// (Auto-connect toggle removed per design)
2025-08-08 08:43:33 +08:00
2025-07-24 11:31:47 +08:00
// Client selector
string [ ] clientNames = mcpClients . clients . Select ( c = > c . name ) . ToArray ( ) ;
EditorGUI . BeginChangeCheck ( ) ;
selectedClientIndex = EditorGUILayout . Popup ( "Select Client" , selectedClientIndex , clientNames , GUILayout . Height ( 20 ) ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
selectedClientIndex = Mathf . Clamp ( selectedClientIndex , 0 , mcpClients . clients . Count - 1 ) ;
}
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
EditorGUILayout . Space ( 10 ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
if ( mcpClients . clients . Count > 0 & & selectedClientIndex < mcpClients . clients . Count )
{
McpClient selectedClient = mcpClients . clients [ selectedClientIndex ] ;
DrawClientConfigurationCompact ( selectedClient ) ;
}
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
EditorGUILayout . Space ( 5 ) ;
EditorGUILayout . EndVertical ( ) ;
}
2025-03-20 19:24:31 +08:00
2025-08-08 08:43:33 +08:00
private void AutoFirstRunSetup ( )
{
try
{
// Project-scoped one-time flag
string projectPath = Application . dataPath ? ? string . Empty ;
2025-08-21 03:59:49 +08:00
string key = $"MCPForUnity.AutoRegistered.{ComputeSha1(projectPath)}" ;
2025-08-08 08:43:33 +08:00
if ( EditorPrefs . GetBool ( key , false ) )
{
return ;
}
// Attempt client registration using discovered Python server dir
2025-08-21 03:59:49 +08:00
pythonDirOverride ? ? = EditorPrefs . GetString ( "MCPForUnity.PythonDirOverride" , null ) ;
2025-08-08 08:43:33 +08:00
string pythonDir = ! string . IsNullOrEmpty ( pythonDirOverride ) ? pythonDirOverride : FindPackagePythonDirectory ( ) ;
if ( ! string . IsNullOrEmpty ( pythonDir ) & & File . Exists ( Path . Combine ( pythonDir , "server.py" ) ) )
{
bool anyRegistered = false ;
foreach ( McpClient client in mcpClients . clients )
{
try
{
if ( client . mcpType = = McpTypes . ClaudeCode )
{
2025-08-12 23:32:51 +08:00
// Only attempt if Claude CLI is present
if ( ! IsClaudeConfigured ( ) & & ! string . IsNullOrEmpty ( ExecPath . ResolveClaude ( ) ) )
2025-08-08 08:43:33 +08:00
{
RegisterWithClaudeCode ( pythonDir ) ;
anyRegistered = true ;
}
}
else
{
2025-09-27 06:05:30 +08:00
CheckMcpConfiguration ( client ) ;
bool alreadyConfigured = client . status = = McpStatus . Configured ;
if ( ! alreadyConfigured )
2025-08-08 08:43:33 +08:00
{
ConfigureMcpClient ( client ) ;
anyRegistered = true ;
}
}
}
catch ( Exception ex )
{
2025-08-24 13:13:47 +08:00
MCPForUnity . Editor . Helpers . McpLog . Warn ( $"Auto-setup client '{client.name}' failed: {ex.Message}" ) ;
2025-08-08 08:43:33 +08:00
}
}
2025-10-01 04:25:33 +08:00
lastClientRegisteredOk = anyRegistered
| | IsCursorConfigured ( pythonDir )
| | CodexConfigHelper . IsCodexConfigured ( pythonDir )
| | IsClaudeConfigured ( ) ;
2025-08-08 08:43:33 +08:00
}
// Ensure the bridge is listening and has a fresh saved port
2025-08-21 03:59:49 +08:00
if ( ! MCPForUnityBridge . IsRunning )
2025-08-08 08:43:33 +08:00
{
try
{
2025-08-21 03:59:49 +08:00
MCPForUnityBridge . StartAutoConnect ( ) ;
isUnityBridgeRunning = MCPForUnityBridge . IsRunning ;
2025-08-08 08:43:33 +08:00
Repaint ( ) ;
}
catch ( Exception ex )
{
2025-08-24 13:13:47 +08:00
MCPForUnity . Editor . Helpers . McpLog . Warn ( $"Auto-setup StartAutoConnect failed: {ex.Message}" ) ;
2025-08-08 08:43:33 +08:00
}
}
// Verify bridge with a quick ping
2025-08-21 03:59:49 +08:00
lastBridgeVerifiedOk = VerifyBridgePing ( MCPForUnityBridge . GetCurrentPort ( ) ) ;
2025-08-08 08:43:33 +08:00
EditorPrefs . SetBool ( key , true ) ;
}
catch ( Exception e )
{
2025-08-24 13:13:47 +08:00
MCPForUnity . Editor . Helpers . McpLog . Warn ( $"MCP for Unity auto-setup skipped: {e.Message}" ) ;
2025-08-08 08:43:33 +08:00
}
}
private static string ComputeSha1 ( string input )
{
try
{
using SHA1 sha1 = SHA1 . Create ( ) ;
byte [ ] bytes = Encoding . UTF8 . GetBytes ( input ? ? string . Empty ) ;
byte [ ] hash = sha1 . ComputeHash ( bytes ) ;
StringBuilder sb = new StringBuilder ( hash . Length * 2 ) ;
foreach ( byte b in hash )
{
sb . Append ( b . ToString ( "x2" ) ) ;
}
return sb . ToString ( ) ;
}
catch
{
return "" ;
}
}
private void RunSetupNow ( )
{
// Force a one-shot setup regardless of first-run flag
try
{
2025-08-21 03:59:49 +08:00
pythonDirOverride ? ? = EditorPrefs . GetString ( "MCPForUnity.PythonDirOverride" , null ) ;
2025-08-08 08:43:33 +08:00
string pythonDir = ! string . IsNullOrEmpty ( pythonDirOverride ) ? pythonDirOverride : FindPackagePythonDirectory ( ) ;
if ( string . IsNullOrEmpty ( pythonDir ) | | ! File . Exists ( Path . Combine ( pythonDir , "server.py" ) ) )
{
EditorUtility . DisplayDialog ( "Setup" , "Python server not found. Please select UnityMcpServer/src." , "OK" ) ;
return ;
}
bool anyRegistered = false ;
foreach ( McpClient client in mcpClients . clients )
{
try
{
if ( client . mcpType = = McpTypes . ClaudeCode )
{
if ( ! IsClaudeConfigured ( ) )
{
RegisterWithClaudeCode ( pythonDir ) ;
anyRegistered = true ;
}
}
else
{
2025-09-27 06:05:30 +08:00
CheckMcpConfiguration ( client ) ;
bool alreadyConfigured = client . status = = McpStatus . Configured ;
if ( ! alreadyConfigured )
2025-08-08 08:43:33 +08:00
{
ConfigureMcpClient ( client ) ;
anyRegistered = true ;
}
}
}
catch ( Exception ex )
{
UnityEngine . Debug . LogWarning ( $"Setup client '{client.name}' failed: {ex.Message}" ) ;
}
}
2025-10-01 04:25:33 +08:00
lastClientRegisteredOk = anyRegistered
| | IsCursorConfigured ( pythonDir )
| | CodexConfigHelper . IsCodexConfigured ( pythonDir )
| | IsClaudeConfigured ( ) ;
2025-08-08 08:43:33 +08:00
// Restart/ensure bridge
2025-08-21 03:59:49 +08:00
MCPForUnityBridge . StartAutoConnect ( ) ;
isUnityBridgeRunning = MCPForUnityBridge . IsRunning ;
2025-08-08 08:43:33 +08:00
// Verify
2025-08-21 03:59:49 +08:00
lastBridgeVerifiedOk = VerifyBridgePing ( MCPForUnityBridge . GetCurrentPort ( ) ) ;
2025-08-08 08:43:33 +08:00
Repaint ( ) ;
}
catch ( Exception e )
{
EditorUtility . DisplayDialog ( "Setup Failed" , e . Message , "OK" ) ;
}
}
2025-10-01 04:25:33 +08:00
private static bool IsCursorConfigured ( string pythonDir )
{
try
{
string configPath = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows )
? Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . UserProfile ) ,
2025-08-08 08:43:33 +08:00
".cursor" , "mcp.json" )
2025-10-01 04:25:33 +08:00
: Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . UserProfile ) ,
2025-08-08 08:43:33 +08:00
".cursor" , "mcp.json" ) ;
if ( ! File . Exists ( configPath ) ) return false ;
string json = File . ReadAllText ( configPath ) ;
dynamic cfg = JsonConvert . DeserializeObject ( json ) ;
var servers = cfg ? . mcpServers ;
if ( servers = = null ) return false ;
var unity = servers . unityMCP ? ? servers . UnityMCP ;
if ( unity = = null ) return false ;
var args = unity . args ;
if ( args = = null ) return false ;
2025-08-14 09:13:25 +08:00
// 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 ( ) ;
2025-09-27 06:05:30 +08:00
string dir = McpConfigFileHelper . ExtractDirectoryArg ( strArgs ) ;
2025-08-14 09:13:25 +08:00
if ( string . IsNullOrEmpty ( dir ) ) return false ;
2025-09-27 06:05:30 +08:00
return McpConfigFileHelper . PathsEqual ( dir , pythonDir ) ;
2025-08-08 08:43:33 +08:00
}
catch { return false ; }
}
private static bool IsClaudeConfigured ( )
{
try
{
2025-08-13 03:14:48 +08:00
string claudePath = ExecPath . ResolveClaude ( ) ;
if ( string . IsNullOrEmpty ( claudePath ) ) return false ;
// Only prepend PATH on Unix
string pathPrepend = null ;
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) | | RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
{
pathPrepend = RuntimeInformation . IsOSPlatform ( OSPlatform . OSX )
? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
: "/usr/local/bin:/usr/bin:/bin" ;
}
if ( ! ExecPath . TryRun ( claudePath , "mcp list" , workingDir : null , out var stdout , out var stderr , 5000 , pathPrepend ) )
{
return false ;
}
return ( stdout ? ? string . Empty ) . IndexOf ( "UnityMCP" , StringComparison . OrdinalIgnoreCase ) > = 0 ;
2025-08-08 08:43:33 +08:00
}
catch { return false ; }
}
private static bool VerifyBridgePing ( int port )
{
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
// Use strict framed protocol to match bridge (FRAMING=1)
const int ConnectTimeoutMs = 1000 ;
const int FrameTimeoutMs = 30000 ; // match bridge frame I/O timeout
2025-08-08 08:43:33 +08:00
try
{
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
using TcpClient client = new TcpClient ( ) ;
var connectTask = client . ConnectAsync ( IPAddress . Loopback , port ) ;
if ( ! connectTask . Wait ( ConnectTimeoutMs ) ) return false ;
using NetworkStream stream = client . GetStream ( ) ;
try { client . NoDelay = true ; } catch { }
// 1) Read handshake line (ASCII, newline-terminated)
string handshake = ReadLineAscii ( stream , 2000 ) ;
if ( string . IsNullOrEmpty ( handshake ) | | handshake . IndexOf ( "FRAMING=1" , StringComparison . OrdinalIgnoreCase ) < 0 )
{
UnityEngine . Debug . LogWarning ( "MCP for Unity: Bridge handshake missing FRAMING=1" ) ;
return false ;
}
// 2) Send framed "ping"
byte [ ] payload = Encoding . UTF8 . GetBytes ( "ping" ) ;
WriteFrame ( stream , payload , FrameTimeoutMs ) ;
// 3) Read framed response and check for pong
string response = ReadFrameUtf8 ( stream , FrameTimeoutMs ) ;
bool ok = ! string . IsNullOrEmpty ( response ) & & response . IndexOf ( "pong" , StringComparison . OrdinalIgnoreCase ) > = 0 ;
if ( ! ok )
{
UnityEngine . Debug . LogWarning ( $"MCP for Unity: Framed ping failed; response='{response}'" ) ;
}
return ok ;
2025-08-08 08:43:33 +08:00
}
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
catch ( Exception ex )
{
UnityEngine . Debug . LogWarning ( $"MCP for Unity: VerifyBridgePing error: {ex.Message}" ) ;
return false ;
}
}
// Minimal framing helpers (8-byte big-endian length prefix), blocking with timeouts
private static void WriteFrame ( NetworkStream stream , byte [ ] payload , int timeoutMs )
{
if ( payload = = null ) throw new ArgumentNullException ( nameof ( payload ) ) ;
if ( payload . LongLength < 1 ) throw new IOException ( "Zero-length frames are not allowed" ) ;
byte [ ] header = new byte [ 8 ] ;
ulong len = ( ulong ) payload . LongLength ;
header [ 0 ] = ( byte ) ( len > > 56 ) ;
header [ 1 ] = ( byte ) ( len > > 48 ) ;
header [ 2 ] = ( byte ) ( len > > 40 ) ;
header [ 3 ] = ( byte ) ( len > > 32 ) ;
header [ 4 ] = ( byte ) ( len > > 24 ) ;
header [ 5 ] = ( byte ) ( len > > 16 ) ;
header [ 6 ] = ( byte ) ( len > > 8 ) ;
header [ 7 ] = ( byte ) ( len ) ;
stream . WriteTimeout = timeoutMs ;
stream . Write ( header , 0 , header . Length ) ;
stream . Write ( payload , 0 , payload . Length ) ;
}
private static string ReadFrameUtf8 ( NetworkStream stream , int timeoutMs )
{
byte [ ] header = ReadExact ( stream , 8 , timeoutMs ) ;
ulong len = ( ( ulong ) header [ 0 ] < < 56 )
| ( ( ulong ) header [ 1 ] < < 48 )
| ( ( ulong ) header [ 2 ] < < 40 )
| ( ( ulong ) header [ 3 ] < < 32 )
| ( ( ulong ) header [ 4 ] < < 24 )
| ( ( ulong ) header [ 5 ] < < 16 )
| ( ( ulong ) header [ 6 ] < < 8 )
| header [ 7 ] ;
if ( len = = 0 UL ) throw new IOException ( "Zero-length frames are not allowed" ) ;
if ( len > int . MaxValue ) throw new IOException ( "Frame too large" ) ;
byte [ ] payload = ReadExact ( stream , ( int ) len , timeoutMs ) ;
return Encoding . UTF8 . GetString ( payload ) ;
}
private static byte [ ] ReadExact ( NetworkStream stream , int count , int timeoutMs )
{
byte [ ] buffer = new byte [ count ] ;
int offset = 0 ;
stream . ReadTimeout = timeoutMs ;
while ( offset < count )
{
int read = stream . Read ( buffer , offset , count - offset ) ;
if ( read < = 0 ) throw new IOException ( "Connection closed before reading expected bytes" ) ;
offset + = read ;
}
return buffer ;
}
private static string ReadLineAscii ( NetworkStream stream , int timeoutMs , int maxLen = 512 )
{
stream . ReadTimeout = timeoutMs ;
using var ms = new MemoryStream ( ) ;
byte [ ] one = new byte [ 1 ] ;
while ( ms . Length < maxLen )
{
int n = stream . Read ( one , 0 , 1 ) ;
if ( n < = 0 ) break ;
if ( one [ 0 ] = = ( byte ) '\n' ) break ;
ms . WriteByte ( one [ 0 ] ) ;
}
return Encoding . ASCII . GetString ( ms . ToArray ( ) ) ;
2025-08-08 08:43:33 +08:00
}
2025-07-24 11:31:47 +08:00
private void DrawClientConfigurationCompact ( McpClient mcpClient )
{
2025-10-01 04:25:33 +08:00
// Special pre-check for Claude Code: if CLI missing, reflect in status UI
if ( mcpClient . mcpType = = McpTypes . ClaudeCode )
{
string claudeCheck = ExecPath . ResolveClaude ( ) ;
if ( string . IsNullOrEmpty ( claudeCheck ) )
{
mcpClient . configStatus = "Claude Not Found" ;
mcpClient . status = McpStatus . NotConfigured ;
}
}
// Pre-check for clients that require uv (all except Claude Code)
bool uvRequired = mcpClient . mcpType ! = McpTypes . ClaudeCode ;
bool uvMissingEarly = false ;
if ( uvRequired )
{
string uvPathEarly = FindUvPath ( ) ;
if ( string . IsNullOrEmpty ( uvPathEarly ) )
{
uvMissingEarly = true ;
mcpClient . configStatus = "uv Not Found" ;
mcpClient . status = McpStatus . NotConfigured ;
}
}
2025-08-13 11:56:22 +08:00
2025-07-24 11:31:47 +08:00
// Status display
EditorGUILayout . BeginHorizontal ( ) ;
Rect statusRect = GUILayoutUtility . GetRect ( 0 , 28 , GUILayout . Width ( 24 ) ) ;
Color statusColor = GetStatusColor ( mcpClient . status ) ;
DrawStatusDot ( statusRect , statusColor , 16 ) ;
2025-10-01 04:25:33 +08:00
2025-07-24 11:31:47 +08:00
GUIStyle clientStatusStyle = new GUIStyle ( EditorStyles . label )
{
fontSize = 12 ,
fontStyle = FontStyle . Bold
} ;
EditorGUILayout . LabelField ( mcpClient . configStatus , clientStatusStyle , GUILayout . Height ( 28 ) ) ;
EditorGUILayout . EndHorizontal ( ) ;
2025-10-01 04:25:33 +08:00
// When Claude CLI is missing, show a clear install hint directly below status
if ( mcpClient . mcpType = = McpTypes . ClaudeCode & & string . IsNullOrEmpty ( ExecPath . ResolveClaude ( ) ) )
{
GUIStyle installHintStyle = new GUIStyle ( clientStatusStyle ) ;
installHintStyle . normal . textColor = new Color ( 1f , 0.5f , 0f ) ; // orange
EditorGUILayout . BeginHorizontal ( ) ;
GUIContent installText = new GUIContent ( "Make sure Claude Code is installed!" ) ;
Vector2 textSize = installHintStyle . CalcSize ( installText ) ;
EditorGUILayout . LabelField ( installText , installHintStyle , GUILayout . Height ( 22 ) , GUILayout . Width ( textSize . x + 2 ) , GUILayout . ExpandWidth ( false ) ) ;
GUIStyle helpLinkStyle = new GUIStyle ( EditorStyles . linkLabel ) { fontStyle = FontStyle . Bold } ;
GUILayout . Space ( 6 ) ;
if ( GUILayout . Button ( "[HELP]" , helpLinkStyle , GUILayout . Height ( 22 ) , GUILayout . ExpandWidth ( false ) ) )
{
Application . OpenURL ( "https://github.com/CoplayDev/unity-mcp/wiki/Troubleshooting-Unity-MCP-and-Claude-Code" ) ;
}
EditorGUILayout . EndHorizontal ( ) ;
}
EditorGUILayout . Space ( 10 ) ;
// If uv is missing for required clients, show hint and picker then exit early to avoid showing other controls
if ( uvRequired & & uvMissingEarly )
{
GUIStyle installHintStyle2 = new GUIStyle ( EditorStyles . label )
{
fontSize = 12 ,
fontStyle = FontStyle . Bold ,
wordWrap = false
} ;
installHintStyle2 . normal . textColor = new Color ( 1f , 0.5f , 0f ) ;
EditorGUILayout . BeginHorizontal ( ) ;
GUIContent installText2 = new GUIContent ( "Make sure uv is installed!" ) ;
Vector2 sz = installHintStyle2 . CalcSize ( installText2 ) ;
EditorGUILayout . LabelField ( installText2 , installHintStyle2 , GUILayout . Height ( 22 ) , GUILayout . Width ( sz . x + 2 ) , GUILayout . ExpandWidth ( false ) ) ;
GUIStyle helpLinkStyle2 = new GUIStyle ( EditorStyles . linkLabel ) { fontStyle = FontStyle . Bold } ;
GUILayout . Space ( 6 ) ;
if ( GUILayout . Button ( "[HELP]" , helpLinkStyle2 , GUILayout . Height ( 22 ) , GUILayout . ExpandWidth ( false ) ) )
{
Application . OpenURL ( "https://github.com/CoplayDev/unity-mcp/wiki/Troubleshooting-Unity-MCP-and-Cursor,-VSCode-&-Windsurf" ) ;
}
EditorGUILayout . EndHorizontal ( ) ;
EditorGUILayout . Space ( 8 ) ;
EditorGUILayout . BeginHorizontal ( ) ;
if ( GUILayout . Button ( "Choose uv Install Location" , GUILayout . Width ( 260 ) , GUILayout . Height ( 22 ) ) )
{
string suggested = RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ? "/opt/homebrew/bin" : Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) ;
string picked = EditorUtility . OpenFilePanel ( "Select 'uv' binary" , suggested , "" ) ;
if ( ! string . IsNullOrEmpty ( picked ) )
{
EditorPrefs . SetString ( "MCPForUnity.UvPath" , picked ) ;
ConfigureMcpClient ( mcpClient ) ;
Repaint ( ) ;
}
}
EditorGUILayout . EndHorizontal ( ) ;
return ;
}
2025-07-24 11:31:47 +08:00
// Action buttons in horizontal layout
EditorGUILayout . BeginHorizontal ( ) ;
2025-10-01 04:25:33 +08:00
2025-05-12 09:25:21 +08:00
if ( mcpClient . mcpType = = McpTypes . VSCode )
2025-03-20 19:24:31 +08:00
{
2025-07-24 11:31:47 +08:00
if ( GUILayout . Button ( "Auto Configure" , GUILayout . Height ( 32 ) ) )
2025-05-12 09:25:21 +08:00
{
ConfigureMcpClient ( mcpClient ) ;
}
2025-07-24 11:31:47 +08:00
}
2025-10-01 04:25:33 +08:00
else if ( mcpClient . mcpType = = McpTypes . ClaudeCode )
{
bool claudeAvailable = ! string . IsNullOrEmpty ( ExecPath . ResolveClaude ( ) ) ;
if ( claudeAvailable )
{
bool isConfigured = mcpClient . status = = McpStatus . Configured ;
string buttonText = isConfigured ? "Unregister MCP for Unity with Claude Code" : "Register with Claude Code" ;
if ( GUILayout . Button ( buttonText , GUILayout . Height ( 32 ) ) )
{
if ( isConfigured )
{
UnregisterWithClaudeCode ( ) ;
}
else
{
string pythonDir = FindPackagePythonDirectory ( ) ;
RegisterWithClaudeCode ( pythonDir ) ;
}
}
// Hide the picker once a valid binary is available
EditorGUILayout . EndHorizontal ( ) ;
EditorGUILayout . BeginHorizontal ( ) ;
GUIStyle pathLabelStyle = new GUIStyle ( EditorStyles . miniLabel ) { wordWrap = true } ;
string resolvedClaude = ExecPath . ResolveClaude ( ) ;
EditorGUILayout . LabelField ( $"Claude CLI: {resolvedClaude}" , pathLabelStyle ) ;
EditorGUILayout . EndHorizontal ( ) ;
EditorGUILayout . BeginHorizontal ( ) ;
}
// CLI picker row (only when not found)
EditorGUILayout . EndHorizontal ( ) ;
EditorGUILayout . BeginHorizontal ( ) ;
if ( ! claudeAvailable )
{
// Only show the picker button in not-found state (no redundant "not found" label)
if ( GUILayout . Button ( "Choose Claude Install Location" , GUILayout . Width ( 260 ) , GUILayout . Height ( 22 ) ) )
{
string suggested = RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ? "/opt/homebrew/bin" : Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) ;
string picked = EditorUtility . OpenFilePanel ( "Select 'claude' CLI" , suggested , "" ) ;
if ( ! string . IsNullOrEmpty ( picked ) )
{
ExecPath . SetClaudeCliPath ( picked ) ;
// Auto-register after setting a valid path
string pythonDir = FindPackagePythonDirectory ( ) ;
RegisterWithClaudeCode ( pythonDir ) ;
Repaint ( ) ;
}
}
}
EditorGUILayout . EndHorizontal ( ) ;
EditorGUILayout . BeginHorizontal ( ) ;
}
2025-07-24 11:31:47 +08:00
else
{
if ( GUILayout . Button ( $"Auto Configure" , GUILayout . Height ( 32 ) ) )
2025-05-12 09:25:21 +08:00
{
2025-07-24 11:31:47 +08:00
ConfigureMcpClient ( mcpClient ) ;
}
}
2025-10-01 04:25:33 +08:00
2025-07-29 01:45:07 +08:00
if ( mcpClient . mcpType ! = McpTypes . ClaudeCode )
2025-07-24 11:31:47 +08:00
{
2025-07-29 01:45:07 +08:00
if ( GUILayout . Button ( "Manual Setup" , GUILayout . Height ( 32 ) ) )
2025-07-24 11:31:47 +08:00
{
2025-07-29 01:45:07 +08:00
string configPath = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows )
? mcpClient . windowsConfigPath
: mcpClient . linuxConfigPath ;
2025-10-01 04:25:33 +08:00
2025-07-29 01:45:07 +08:00
if ( mcpClient . mcpType = = McpTypes . VSCode )
2025-05-12 09:25:21 +08:00
{
2025-07-29 01:45:07 +08:00
string pythonDir = FindPackagePythonDirectory ( ) ;
2025-07-29 02:55:08 +08:00
string uvPath = FindUvPath ( ) ;
if ( uvPath = = null )
{
UnityEngine . Debug . LogError ( "UV package manager not found. Cannot configure VSCode." ) ;
return ;
}
2025-08-25 05:18:08 +08:00
// VSCode now reads from mcp.json with a top-level "servers" block
2025-07-29 01:45:07 +08:00
var vscodeConfig = new
2025-05-12 09:25:21 +08:00
{
2025-08-25 05:18:08 +08:00
servers = new
2025-05-12 09:25:21 +08:00
{
2025-08-25 05:18:08 +08:00
unityMCP = new
2025-05-12 09:25:21 +08:00
{
2025-08-25 05:18:08 +08:00
command = uvPath ,
args = new [ ] { "run" , "--directory" , pythonDir , "server.py" }
2025-05-12 09:25:21 +08:00
}
}
2025-07-29 01:45:07 +08:00
} ;
JsonSerializerSettings jsonSettings = new ( ) { Formatting = Formatting . Indented } ;
string manualConfigJson = JsonConvert . SerializeObject ( vscodeConfig , jsonSettings ) ;
VSCodeManualSetupWindow . ShowWindow ( configPath , manualConfigJson ) ;
}
else
{
ShowManualInstructionsWindow ( configPath , mcpClient ) ;
}
2025-05-12 09:25:21 +08:00
}
2025-03-20 19:24:31 +08:00
}
2025-10-01 04:25:33 +08:00
2025-03-20 19:24:31 +08:00
EditorGUILayout . EndHorizontal ( ) ;
2025-10-01 04:25:33 +08:00
EditorGUILayout . Space ( 8 ) ;
// Quick info (hide when Claude is not found to avoid confusion)
bool hideConfigInfo =
( mcpClient . mcpType = = McpTypes . ClaudeCode & & string . IsNullOrEmpty ( ExecPath . ResolveClaude ( ) ) )
| | ( ( mcpClient . mcpType ! = McpTypes . ClaudeCode ) & & string . IsNullOrEmpty ( FindUvPath ( ) ) ) ;
if ( ! hideConfigInfo )
{
GUIStyle configInfoStyle = new GUIStyle ( EditorStyles . miniLabel )
{
fontSize = 10
} ;
EditorGUILayout . LabelField ( $"Config: {Path.GetFileName(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? mcpClient.windowsConfigPath : mcpClient.linuxConfigPath)}" , configInfoStyle ) ;
}
2025-03-20 19:24:31 +08:00
}
private void ToggleUnityBridge ( )
{
if ( isUnityBridgeRunning )
{
2025-08-21 03:59:49 +08:00
MCPForUnityBridge . Stop ( ) ;
2025-03-20 19:24:31 +08:00
}
else
{
2025-08-21 03:59:49 +08:00
MCPForUnityBridge . Start ( ) ;
2025-03-20 19:24:31 +08:00
}
2025-08-08 06:32:03 +08:00
// Reflect the actual state post-operation (avoid optimistic toggle)
2025-08-21 03:59:49 +08:00
isUnityBridgeRunning = MCPForUnityBridge . IsRunning ;
2025-08-08 06:32:03 +08:00
Repaint ( ) ;
2025-03-20 19:24:31 +08:00
}
// New method to show manual instructions without changing status
2025-10-01 04:25:33 +08:00
private void ShowManualInstructionsWindow ( string configPath , McpClient mcpClient )
{
// Get the Python directory path using Package Manager API
string pythonDir = FindPackagePythonDirectory ( ) ;
// Build manual JSON centrally using the shared builder
string uvPathForManual = FindUvPath ( ) ;
if ( uvPathForManual = = null )
{
UnityEngine . Debug . LogError ( "UV package manager not found. Cannot generate manual configuration." ) ;
return ;
}
string manualConfig = mcpClient ? . mcpType = = McpTypes . Codex
? CodexConfigHelper . BuildCodexServerBlock ( uvPathForManual , McpConfigFileHelper . ResolveServerDirectory ( pythonDir , null ) ) . TrimEnd ( ) + Environment . NewLine
: ConfigJsonBuilder . BuildManualConfigJson ( uvPathForManual , pythonDir , mcpClient ) ;
ManualConfigEditorWindow . ShowWindow ( configPath , manualConfig , mcpClient ) ;
}
2025-08-14 08:46:07 +08:00
2025-09-27 06:05:30 +08:00
private string FindPackagePythonDirectory ( )
2025-03-20 19:24:31 +08:00
{
2025-10-04 04:43:40 +08:00
// Use shared helper for consistent path resolution across both windows
return McpPathResolver . FindPackagePythonDirectory ( debugLogsEnabled ) ;
2025-08-08 08:43:33 +08:00
}
2025-10-01 04:25:33 +08:00
private string ConfigureMcpClient ( McpClient mcpClient )
{
try
{
2025-10-04 04:43:40 +08:00
// Use shared helper for consistent config path resolution
string configPath = McpConfigurationHelper . GetClientConfigPath ( mcpClient ) ;
2025-03-20 19:24:31 +08:00
// Create directory if it doesn't exist
2025-10-04 04:43:40 +08:00
McpConfigurationHelper . EnsureConfigDirectoryExists ( configPath ) ;
2025-03-20 19:24:31 +08:00
2025-10-04 04:43:40 +08:00
// Find the server.py file location using shared helper
2025-10-01 04:25:33 +08:00
string pythonDir = FindPackagePythonDirectory ( ) ;
2025-03-20 19:24:31 +08:00
2025-10-01 04:25:33 +08:00
if ( pythonDir = = null | | ! File . Exists ( Path . Combine ( pythonDir , "server.py" ) ) )
{
ShowManualInstructionsWindow ( configPath , mcpClient ) ;
return "Manual Configuration Required" ;
}
2025-03-20 19:24:31 +08:00
2025-10-01 04:25:33 +08:00
string result = mcpClient . mcpType = = McpTypes . Codex
2025-10-04 04:43:40 +08:00
? McpConfigurationHelper . ConfigureCodexClient ( pythonDir , configPath , mcpClient )
: McpConfigurationHelper . WriteMcpConfiguration ( pythonDir , configPath , mcpClient ) ;
2025-03-20 19:24:31 +08:00
2025-10-01 04:25:33 +08:00
// Update the client status after successful configuration
if ( result = = "Configured successfully" )
{
mcpClient . SetStatus ( McpStatus . Configured ) ;
2025-03-20 19:24:31 +08:00
}
return result ;
}
catch ( Exception e )
{
// Determine the config file path based on OS for error message
string configPath = "" ;
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
{
configPath = mcpClient . windowsConfigPath ;
}
2025-04-08 18:14:13 +08:00
else if (
RuntimeInformation . IsOSPlatform ( OSPlatform . OSX )
2025-08-26 06:22:04 +08:00
)
{
configPath = string . IsNullOrEmpty ( mcpClient . macConfigPath )
? mcpClient . linuxConfigPath
: mcpClient . macConfigPath ;
}
else if (
RuntimeInformation . IsOSPlatform ( OSPlatform . Linux )
2025-04-08 18:14:13 +08:00
)
2025-03-20 19:24:31 +08:00
{
configPath = mcpClient . linuxConfigPath ;
}
ShowManualInstructionsWindow ( configPath , mcpClient ) ;
2025-07-29 01:45:07 +08:00
UnityEngine . Debug . LogError (
2025-04-08 18:14:13 +08:00
$"Failed to configure {mcpClient.name}: {e.Message}\n{e.StackTrace}"
) ;
2025-03-20 19:24:31 +08:00
return $"Failed to configure {mcpClient.name}" ;
2025-10-01 04:25:33 +08:00
}
}
2025-07-24 11:31:47 +08:00
private void LoadValidationLevelSetting ( )
{
2025-08-21 03:59:49 +08:00
string savedLevel = EditorPrefs . GetString ( "MCPForUnity_ScriptValidationLevel" , "standard" ) ;
2025-07-24 11:31:47 +08:00
validationLevelIndex = savedLevel . ToLower ( ) switch
{
"basic" = > 0 ,
"standard" = > 1 ,
"comprehensive" = > 2 ,
"strict" = > 3 ,
_ = > 1 // Default to Standard
} ;
}
private void SaveValidationLevelSetting ( )
{
string levelString = validationLevelIndex switch
{
0 = > "basic" ,
1 = > "standard" ,
2 = > "comprehensive" ,
3 = > "strict" ,
_ = > "standard"
} ;
2025-08-21 03:59:49 +08:00
EditorPrefs . SetString ( "MCPForUnity_ScriptValidationLevel" , levelString ) ;
2025-07-24 11:31:47 +08:00
}
private string GetValidationLevelDescription ( int index )
{
return index switch
{
0 = > "Only basic syntax checks (braces, quotes, comments)" ,
1 = > "Syntax checks + Unity best practices and warnings" ,
2 = > "All checks + semantic analysis and performance warnings" ,
3 = > "Full semantic validation with namespace/type resolution (requires Roslyn)" ,
_ = > "Standard validation"
} ;
}
2025-03-20 19:24:31 +08:00
private void CheckMcpConfiguration ( McpClient mcpClient )
{
try
{
2025-07-29 01:45:07 +08:00
// Special handling for Claude Code
if ( mcpClient . mcpType = = McpTypes . ClaudeCode )
{
CheckClaudeCodeConfiguration ( mcpClient ) ;
return ;
}
2025-10-01 04:25:33 +08:00
2025-10-04 04:43:40 +08:00
// Use shared helper for consistent config path resolution
string configPath = McpConfigurationHelper . GetClientConfigPath ( mcpClient ) ;
2025-03-20 19:24:31 +08:00
if ( ! File . Exists ( configPath ) )
{
mcpClient . SetStatus ( McpStatus . NotConfigured ) ;
return ;
}
string configJson = File . ReadAllText ( configPath ) ;
2025-08-08 08:43:33 +08:00
// Use the same path resolution as configuration to avoid false "Incorrect Path" in dev mode
string pythonDir = FindPackagePythonDirectory ( ) ;
2025-10-01 04:25:33 +08:00
2025-05-12 16:05:56 +08:00
// Use switch statement to handle different client types, extracting common logic
string [ ] args = null ;
bool configExists = false ;
2025-10-01 04:25:33 +08:00
switch ( mcpClient . mcpType )
{
case McpTypes . VSCode :
dynamic config = JsonConvert . DeserializeObject ( configJson ) ;
// New schema: top-level servers
if ( config ? . servers ? . unityMCP ! = null )
{
args = config . servers . unityMCP . args . ToObject < string [ ] > ( ) ;
configExists = true ;
}
// Back-compat: legacy mcp.servers
else if ( config ? . mcp ? . servers ? . unityMCP ! = null )
{
args = config . mcp . servers . unityMCP . args . ToObject < string [ ] > ( ) ;
configExists = true ;
}
break ;
case McpTypes . Codex :
if ( CodexConfigHelper . TryParseCodexServer ( configJson , out _ , out var codexArgs ) )
{
args = codexArgs ;
configExists = true ;
}
break ;
default :
// Standard MCP configuration check for Claude Desktop, Cursor, etc.
McpConfig standardConfig = JsonConvert . DeserializeObject < McpConfig > ( configJson ) ;
2025-05-12 09:25:21 +08:00
if ( standardConfig ? . mcpServers ? . unityMCP ! = null )
{
2025-05-12 16:05:56 +08:00
args = standardConfig . mcpServers . unityMCP . args ;
configExists = true ;
2025-05-12 09:25:21 +08:00
}
break ;
2025-03-20 19:24:31 +08:00
}
2025-10-01 04:25:33 +08:00
2025-05-12 16:05:56 +08:00
// Common logic for checking configuration status
if ( configExists )
{
2025-09-27 06:05:30 +08:00
string configuredDir = McpConfigFileHelper . ExtractDirectoryArg ( args ) ;
bool matches = ! string . IsNullOrEmpty ( configuredDir ) & & McpConfigFileHelper . PathsEqual ( configuredDir , pythonDir ) ;
2025-08-10 06:08:28 +08:00
if ( matches )
2025-05-12 16:05:56 +08:00
{
mcpClient . SetStatus ( McpStatus . Configured ) ;
}
else
{
2025-08-10 06:08:28 +08:00
// Attempt auto-rewrite once if the package path changed
try
{
2025-09-27 06:05:30 +08:00
string rewriteResult = mcpClient . mcpType = = McpTypes . Codex
2025-10-04 04:43:40 +08:00
? McpConfigurationHelper . ConfigureCodexClient ( pythonDir , configPath , mcpClient )
: McpConfigurationHelper . WriteMcpConfiguration ( pythonDir , configPath , mcpClient ) ;
2025-08-10 06:08:28 +08:00
if ( rewriteResult = = "Configured successfully" )
{
if ( debugLogsEnabled )
{
2025-09-03 00:36:50 +08:00
MCPForUnity . Editor . Helpers . McpLog . Info ( $"Auto-updated MCP config for '{mcpClient.name}' to new path: {pythonDir}" , always : false ) ;
2025-08-10 06:08:28 +08:00
}
mcpClient . SetStatus ( McpStatus . Configured ) ;
}
else
{
mcpClient . SetStatus ( McpStatus . IncorrectPath ) ;
}
}
catch ( Exception ex )
{
mcpClient . SetStatus ( McpStatus . IncorrectPath ) ;
if ( debugLogsEnabled )
{
2025-08-21 03:59:49 +08:00
UnityEngine . Debug . LogWarning ( $"MCP for Unity: Auto-config rewrite failed for '{mcpClient.name}': {ex.Message}" ) ;
2025-08-10 06:08:28 +08:00
}
}
2025-05-12 16:05:56 +08:00
}
}
else
{
mcpClient . SetStatus ( McpStatus . MissingConfig ) ;
}
2025-03-20 19:24:31 +08:00
}
catch ( Exception e )
{
mcpClient . SetStatus ( McpStatus . Error , e . Message ) ;
}
}
2025-07-29 01:45:07 +08:00
private void RegisterWithClaudeCode ( string pythonDir )
{
2025-08-12 23:32:51 +08:00
// Resolve claude and uv; then run register command
string claudePath = ExecPath . ResolveClaude ( ) ;
if ( string . IsNullOrEmpty ( claudePath ) )
2025-07-29 01:45:07 +08:00
{
2025-08-21 03:59:49 +08:00
UnityEngine . Debug . LogError ( "MCP for Unity: Claude CLI not found. Set a path in this window or install the CLI, then try again." ) ;
2025-08-12 23:32:51 +08:00
return ;
2025-07-29 01:45:07 +08:00
}
2025-08-12 23:32:51 +08:00
string uvPath = ExecPath . ResolveUv ( ) ? ? "uv" ;
2025-07-29 01:45:07 +08:00
2025-08-12 23:32:51 +08:00
// Prefer embedded/dev path when available
string srcDir = ! string . IsNullOrEmpty ( pythonDirOverride ) ? pythonDirOverride : FindPackagePythonDirectory ( ) ;
if ( string . IsNullOrEmpty ( srcDir ) ) srcDir = pythonDir ;
2025-07-29 01:45:07 +08:00
2025-08-12 23:32:51 +08:00
string args = $"mcp add UnityMCP -- \" { uvPath } \ " run --directory \"{srcDir}\" server.py" ;
2025-07-29 01:45:07 +08:00
2025-08-12 23:32:51 +08:00
string projectDir = Path . GetDirectoryName ( Application . dataPath ) ;
2025-08-13 02:56:46 +08:00
// Ensure PATH includes common locations on Unix; on Windows leave PATH as-is
string pathPrepend = null ;
if ( Application . platform = = RuntimePlatform . OSXEditor | | Application . platform = = RuntimePlatform . LinuxEditor )
{
pathPrepend = Application . platform = = RuntimePlatform . OSXEditor
? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
: "/usr/local/bin:/usr/bin:/bin" ;
}
2025-08-12 23:32:51 +08:00
if ( ! ExecPath . TryRun ( claudePath , args , projectDir , out var stdout , out var stderr , 15000 , pathPrepend ) )
2025-07-29 01:45:07 +08:00
{
2025-08-13 03:14:48 +08:00
string combined = ( $"{stdout}\n{stderr}" ) ? ? string . Empty ;
if ( combined . IndexOf ( "already exists" , StringComparison . OrdinalIgnoreCase ) > = 0 )
{
// Treat as success if Claude reports existing registration
var existingClient = mcpClients . clients . FirstOrDefault ( c = > c . mcpType = = McpTypes . ClaudeCode ) ;
if ( existingClient ! = null ) CheckClaudeCodeConfiguration ( existingClient ) ;
Repaint ( ) ;
2025-08-21 03:59:49 +08:00
UnityEngine . Debug . Log ( "<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: MCP for Unity already registered with Claude Code." ) ;
2025-08-13 03:14:48 +08:00
}
else
{
2025-08-21 03:59:49 +08:00
UnityEngine . Debug . LogError ( $"MCP for Unity: Failed to start Claude CLI.\n{stderr}\n{stdout}" ) ;
2025-08-13 03:14:48 +08:00
}
2025-08-12 23:32:51 +08:00
return ;
2025-07-29 01:45:07 +08:00
}
2025-08-12 23:32:51 +08:00
// Update status
var claudeClient = mcpClients . clients . FirstOrDefault ( c = > c . mcpType = = McpTypes . ClaudeCode ) ;
if ( claudeClient ! = null ) CheckClaudeCodeConfiguration ( claudeClient ) ;
Repaint ( ) ;
2025-08-21 03:59:49 +08:00
UnityEngine . Debug . Log ( "<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Registered with Claude Code." ) ;
2025-07-29 01:45:07 +08:00
}
private void UnregisterWithClaudeCode ( )
{
2025-08-12 23:32:51 +08:00
string claudePath = ExecPath . ResolveClaude ( ) ;
if ( string . IsNullOrEmpty ( claudePath ) )
2025-07-29 01:45:07 +08:00
{
2025-08-21 03:59:49 +08:00
UnityEngine . Debug . LogError ( "MCP for Unity: Claude CLI not found. Set a path in this window or install the CLI, then try again." ) ;
2025-08-12 23:32:51 +08:00
return ;
2025-07-29 01:45:07 +08:00
}
2025-08-12 23:32:51 +08:00
string projectDir = Path . GetDirectoryName ( Application . dataPath ) ;
string pathPrepend = Application . platform = = RuntimePlatform . OSXEditor
? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
2025-08-14 03:36:24 +08:00
: null ; // On Windows, don't modify PATH - use system PATH as-is
2025-07-29 01:45:07 +08:00
2025-10-01 04:25:33 +08:00
// Determine if Claude has a "UnityMCP" server registered by using exit codes from `claude mcp get <name>`
string [ ] candidateNamesForGet = { "UnityMCP" , "unityMCP" , "unity-mcp" , "UnityMcpServer" } ;
List < string > existingNames = new List < string > ( ) ;
foreach ( var candidate in candidateNamesForGet )
{
if ( ExecPath . TryRun ( claudePath , $"mcp get {candidate}" , projectDir , out var getStdout , out var getStderr , 7000 , pathPrepend ) )
{
// Success exit code indicates the server exists
existingNames . Add ( candidate ) ;
}
}
if ( existingNames . Count = = 0 )
{
// Nothing to unregister – set status and bail early
var claudeClient = mcpClients . clients . FirstOrDefault ( c = > c . mcpType = = McpTypes . ClaudeCode ) ;
if ( claudeClient ! = null )
{
claudeClient . SetStatus ( McpStatus . NotConfigured ) ;
UnityEngine . Debug . Log ( "Claude CLI reports no MCP for Unity server via 'mcp get' - setting status to NotConfigured and aborting unregister." ) ;
Repaint ( ) ;
}
return ;
}
2025-08-14 03:36:24 +08:00
// Try different possible server names
string [ ] possibleNames = { "UnityMCP" , "unityMCP" , "unity-mcp" , "UnityMcpServer" } ;
bool success = false ;
2025-10-01 04:25:33 +08:00
2025-08-14 03:36:24 +08:00
foreach ( string serverName in possibleNames )
{
if ( ExecPath . TryRun ( claudePath , $"mcp remove {serverName}" , projectDir , out var stdout , out var stderr , 10000 , pathPrepend ) )
{
success = true ;
2025-08-21 03:59:49 +08:00
UnityEngine . Debug . Log ( $"MCP for Unity: Successfully removed MCP server: {serverName}" ) ;
2025-08-14 03:36:24 +08:00
break ;
}
2025-08-14 05:02:19 +08:00
else if ( ! string . IsNullOrEmpty ( stderr ) & &
! stderr . Contains ( "No MCP server found" , StringComparison . OrdinalIgnoreCase ) )
2025-08-14 03:36:24 +08:00
{
// If it's not a "not found" error, log it and stop trying
UnityEngine . Debug . LogWarning ( $"Error removing {serverName}: {stderr}" ) ;
break ;
}
}
if ( success )
2025-08-12 23:32:51 +08:00
{
var claudeClient = mcpClients . clients . FirstOrDefault ( c = > c . mcpType = = McpTypes . ClaudeCode ) ;
if ( claudeClient ! = null )
2025-07-29 01:45:07 +08:00
{
2025-08-14 02:54:07 +08:00
// Optimistically flip to NotConfigured; then verify
claudeClient . SetStatus ( McpStatus . NotConfigured ) ;
2025-08-12 23:32:51 +08:00
CheckClaudeCodeConfiguration ( claudeClient ) ;
2025-07-29 01:45:07 +08:00
}
2025-08-12 23:32:51 +08:00
Repaint ( ) ;
2025-08-21 03:59:49 +08:00
UnityEngine . Debug . Log ( "MCP for Unity: MCP server successfully unregistered from Claude Code." ) ;
2025-07-29 01:45:07 +08:00
}
2025-08-12 23:32:51 +08:00
else
2025-07-29 01:45:07 +08:00
{
2025-08-14 03:36:24 +08:00
// If no servers were found to remove, they're already unregistered
// Force status to NotConfigured and update the UI
UnityEngine . Debug . Log ( "No MCP servers found to unregister - already unregistered." ) ;
2025-08-14 02:54:07 +08:00
var claudeClient = mcpClients . clients . FirstOrDefault ( c = > c . mcpType = = McpTypes . ClaudeCode ) ;
if ( claudeClient ! = null )
{
2025-08-14 03:36:24 +08:00
claudeClient . SetStatus ( McpStatus . NotConfigured ) ;
2025-08-14 02:54:07 +08:00
CheckClaudeCodeConfiguration ( claudeClient ) ;
}
Repaint ( ) ;
2025-07-29 01:45:07 +08:00
}
}
2025-08-25 00:44:39 +08:00
// Removed unused ParseTextOutput
2025-08-14 03:36:24 +08:00
2025-07-29 02:55:08 +08:00
private string FindUvPath ( )
{
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
try { return MCPForUnity . Editor . Helpers . ServerInstaller . FindUvPath ( ) ; } catch { return null ; }
2025-07-29 02:55:08 +08:00
}
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
// Validation and platform-specific scanning are handled by ServerInstaller.FindUvPath()
2025-07-30 04:01:17 +08:00
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
// Windows-specific discovery removed; use ServerInstaller.FindUvPath() instead
2025-07-29 01:45:07 +08:00
2025-08-25 00:44:39 +08:00
// Removed unused FindClaudeCommand
2025-07-29 06:11:23 +08:00
2025-07-29 01:45:07 +08:00
private void CheckClaudeCodeConfiguration ( McpClient mcpClient )
{
try
{
// Get the Unity project directory to check project-specific config
string unityProjectDir = Application . dataPath ;
string projectDir = Path . GetDirectoryName ( unityProjectDir ) ;
2025-10-01 04:25:33 +08:00
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
// Read the global Claude config file (honor macConfigPath on macOS)
string configPath ;
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
configPath = mcpClient . windowsConfigPath ;
else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
configPath = string . IsNullOrEmpty ( mcpClient . macConfigPath ) ? mcpClient . linuxConfigPath : mcpClient . macConfigPath ;
else
configPath = mcpClient . linuxConfigPath ;
2025-10-01 04:25:33 +08:00
2025-08-09 02:23:45 +08:00
if ( debugLogsEnabled )
{
2025-09-03 00:36:50 +08:00
MCPForUnity . Editor . Helpers . McpLog . Info ( $"Checking Claude config at: {configPath}" , always : false ) ;
2025-08-09 02:23:45 +08:00
}
2025-10-01 04:25:33 +08:00
2025-07-29 01:45:07 +08:00
if ( ! File . Exists ( configPath ) )
{
2025-07-30 04:01:17 +08:00
UnityEngine . Debug . LogWarning ( $"Claude config file not found at: {configPath}" ) ;
2025-07-29 01:45:07 +08:00
mcpClient . SetStatus ( McpStatus . NotConfigured ) ;
return ;
}
2025-10-01 04:25:33 +08:00
2025-07-29 01:45:07 +08:00
string configJson = File . ReadAllText ( configPath ) ;
dynamic claudeConfig = JsonConvert . DeserializeObject ( configJson ) ;
2025-10-01 04:25:33 +08:00
2025-08-21 03:59:49 +08:00
// Check for "UnityMCP" server in the mcpServers section (current format)
2025-07-29 01:45:07 +08:00
if ( claudeConfig ? . mcpServers ! = null )
{
var servers = claudeConfig . mcpServers ;
if ( servers . UnityMCP ! = null | | servers . unityMCP ! = null )
{
2025-08-21 03:59:49 +08:00
// Found MCP for Unity configured
2025-07-29 01:45:07 +08:00
mcpClient . SetStatus ( McpStatus . Configured ) ;
return ;
}
}
2025-10-01 04:25:33 +08:00
2025-07-29 01:45:07 +08:00
// Also check if there's a project-specific configuration for this Unity project (legacy format)
if ( claudeConfig ? . projects ! = null )
{
// Look for the project path in the config
foreach ( var project in claudeConfig . projects )
{
string projectPath = project . Name ;
2025-10-01 04:25:33 +08:00
2025-07-30 04:01:17 +08:00
// Normalize paths for comparison (handle forward/back slash differences)
string normalizedProjectPath = Path . GetFullPath ( projectPath ) . TrimEnd ( Path . DirectorySeparatorChar , Path . AltDirectorySeparatorChar ) ;
string normalizedProjectDir = Path . GetFullPath ( projectDir ) . TrimEnd ( Path . DirectorySeparatorChar , Path . AltDirectorySeparatorChar ) ;
2025-10-01 04:25:33 +08:00
2025-07-30 04:01:17 +08:00
if ( string . Equals ( normalizedProjectPath , normalizedProjectDir , StringComparison . OrdinalIgnoreCase ) & & project . Value ? . mcpServers ! = null )
2025-07-29 01:45:07 +08:00
{
2025-08-21 03:59:49 +08:00
// Check for "UnityMCP" (case variations)
2025-07-29 01:45:07 +08:00
var servers = project . Value . mcpServers ;
if ( servers . UnityMCP ! = null | | servers . unityMCP ! = null )
{
2025-08-21 03:59:49 +08:00
// Found MCP for Unity configured for this project
2025-07-29 01:45:07 +08:00
mcpClient . SetStatus ( McpStatus . Configured ) ;
return ;
}
}
}
}
2025-10-01 04:25:33 +08:00
2025-07-29 01:45:07 +08:00
// No configuration found for this project
mcpClient . SetStatus ( McpStatus . NotConfigured ) ;
}
catch ( Exception e )
{
UnityEngine . Debug . LogWarning ( $"Error checking Claude Code config: {e.Message}" ) ;
mcpClient . SetStatus ( McpStatus . Error , e . Message ) ;
}
}
2025-08-09 05:16:25 +08:00
2025-10-19 08:42:18 +08:00
private void ShowMigrationDialogIfNeeded ( )
{
const string dialogShownKey = "MCPForUnity.LegacyMigrationDialogShown" ;
if ( EditorPrefs . GetBool ( dialogShownKey , false ) )
{
return ; // Already shown
}
int result = EditorUtility . DisplayDialogComplex (
"Migration Required" ,
"This is the legacy UnityMcpBridge package.\n\n" +
"Please migrate to the new MCPForUnity package to receive updates and support.\n\n" +
"Migration takes just a few minutes." ,
"View Migration Guide" ,
"Remind Me Later" ,
"I'll Migrate Later"
) ;
if ( result = = 0 ) // View Migration Guide
{
Application . OpenURL ( "https://github.com/CoplayDev/unity-mcp/blob/main/docs/v5_MIGRATION.md" ) ;
EditorPrefs . SetBool ( dialogShownKey , true ) ;
}
else if ( result = = 2 ) // I'll Migrate Later
{
EditorPrefs . SetBool ( dialogShownKey , true ) ;
}
// result == 1 (Remind Me Later) - don't set the flag, show again next time
}
private void DrawMigrationWarningBanner ( )
{
// Warning banner - not dismissible, always visible
EditorGUILayout . Space ( 5 ) ;
Rect bannerRect = EditorGUILayout . GetControlRect ( false , 50 ) ;
EditorGUI . DrawRect ( bannerRect , new Color ( 1f , 0.6f , 0f , 0.3f ) ) ; // Orange background
GUIStyle warningStyle = new GUIStyle ( EditorStyles . boldLabel )
{
fontSize = 13 ,
alignment = TextAnchor . MiddleLeft ,
richText = true
} ;
// Use Unicode warning triangle (same as used elsewhere in codebase at line 647, 652)
string warningText = "\u26A0 <color=#FF8C00>LEGACY PACKAGE:</color> Please migrate to MCPForUnity for updates and support." ;
Rect textRect = new Rect ( bannerRect . x + 15 , bannerRect . y + 8 , bannerRect . width - 180 , bannerRect . height - 16 ) ;
GUI . Label ( textRect , warningText , warningStyle ) ;
// Button on the right
Rect buttonRect = new Rect ( bannerRect . xMax - 160 , bannerRect . y + 10 , 145 , 30 ) ;
if ( GUI . Button ( buttonRect , "View Migration Guide" ) )
{
Application . OpenURL ( "https://github.com/CoplayDev/unity-mcp/blob/main/docs/v5_MIGRATION.md" ) ;
}
EditorGUILayout . Space ( 5 ) ;
}
2025-08-09 05:16:25 +08:00
private bool IsPythonDetected ( )
{
try
{
2025-08-10 03:05:47 +08:00
// Windows-specific Python detection
if ( Application . platform = = RuntimePlatform . WindowsEditor )
2025-08-09 05:16:25 +08:00
{
2025-08-10 03:05:47 +08:00
// Common Windows Python installation paths
string [ ] windowsCandidates =
{
@"C:\Python313\python.exe" ,
@"C:\Python312\python.exe" ,
@"C:\Python311\python.exe" ,
@"C:\Python310\python.exe" ,
@"C:\Python39\python.exe" ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , @"Programs\Python\Python313\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , @"Programs\Python\Python312\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , @"Programs\Python\Python311\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , @"Programs\Python\Python310\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , @"Programs\Python\Python39\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , @"Python313\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , @"Python312\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , @"Python311\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , @"Python310\python.exe" ) ,
Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , @"Python39\python.exe" ) ,
} ;
2025-10-01 04:25:33 +08:00
2025-08-10 03:05:47 +08:00
foreach ( string c in windowsCandidates )
{
if ( File . Exists ( c ) ) return true ;
}
2025-08-09 05:16:25 +08:00
2025-08-10 03:05:47 +08:00
// Try 'where python' command (Windows equivalent of 'which')
var psi = new ProcessStartInfo
{
FileName = "where" ,
Arguments = "python" ,
UseShellExecute = false ,
RedirectStandardOutput = true ,
RedirectStandardError = true ,
CreateNoWindow = true
} ;
using var p = Process . Start ( psi ) ;
string outp = p . StandardOutput . ReadToEnd ( ) . Trim ( ) ;
p . WaitForExit ( 2000 ) ;
if ( p . ExitCode = = 0 & & ! string . IsNullOrEmpty ( outp ) )
{
string [ ] lines = outp . Split ( '\n' ) ;
foreach ( string line in lines )
{
string trimmed = line . Trim ( ) ;
if ( File . Exists ( trimmed ) ) return true ;
}
}
}
else
2025-08-09 05:16:25 +08:00
{
2025-08-10 03:05:47 +08:00
// macOS/Linux detection (existing code)
string home = Environment . GetFolderPath ( Environment . SpecialFolder . UserProfile ) ? ? string . Empty ;
string [ ] candidates =
{
"/opt/homebrew/bin/python3" ,
"/usr/local/bin/python3" ,
"/usr/bin/python3" ,
"/opt/local/bin/python3" ,
Path . Combine ( home , ".local" , "bin" , "python3" ) ,
"/Library/Frameworks/Python.framework/Versions/3.13/bin/python3" ,
"/Library/Frameworks/Python.framework/Versions/3.12/bin/python3" ,
} ;
foreach ( string c in candidates )
{
if ( File . Exists ( c ) ) return true ;
}
// Try 'which python3'
var psi = new ProcessStartInfo
{
FileName = "/usr/bin/which" ,
Arguments = "python3" ,
UseShellExecute = false ,
RedirectStandardOutput = true ,
RedirectStandardError = true ,
CreateNoWindow = true
} ;
using var p = Process . Start ( psi ) ;
string outp = p . StandardOutput . ReadToEnd ( ) . Trim ( ) ;
p . WaitForExit ( 2000 ) ;
if ( p . ExitCode = = 0 & & ! string . IsNullOrEmpty ( outp ) & & File . Exists ( outp ) ) return true ;
}
2025-08-09 05:16:25 +08:00
}
catch { }
return false ;
}
2025-03-20 19:24:31 +08:00
}
2025-04-08 18:14:13 +08:00
}