@@ -19,9 +19,11 @@ namespace MCPForUnity.Editor.Tools
1919{
2020 /// <summary>
2121 /// Runtime compilation tool for MCP Unity.
22- /// Compiles and loads C# code at runtime without triggering domain reload.
22+ /// Compiles and loads C# code at runtime without triggering domain reload via Roslyn Runtime Compilation, where in traditional Unity workflow it would take seconds to reload assets and reset script states for each script change.
2323 /// </summary>
24- [ McpForUnityTool ( "runtime_compilation" ) ]
24+ [ McpForUnityTool (
25+ name : "runtime_compilation" ,
26+ Description = "Enable runtime compilation of C# code within Unity without domain reload via Roslyn." ) ]
2527 public static class ManageRuntimeCompilation
2628 {
2729 private static readonly Dictionary < string , LoadedAssemblyInfo > LoadedAssemblies = new Dictionary < string , LoadedAssemblyInfo > ( ) ;
@@ -42,7 +44,7 @@ public static object HandleCommand(JObject @params)
4244
4345 if ( string . IsNullOrEmpty ( action ) )
4446 {
45- return Response . Error ( "Action parameter is required. Valid actions: compile_and_load, list_loaded, get_types, execute_with_roslyn, get_history, save_history, clear_history" ) ;
47+ return new ErrorResponse ( "Action parameter is required. Valid actions: compile_and_load, list_loaded, get_types, execute_with_roslyn, get_history, save_history, clear_history" ) ;
4648 }
4749
4850 switch ( action )
@@ -69,31 +71,28 @@ public static object HandleCommand(JObject @params)
6971 return ClearCompilationHistory ( ) ;
7072
7173 default :
72- return Response . Error ( $ "Unknown action '{ action } '. Valid actions: compile_and_load, list_loaded, get_types, execute_with_roslyn, get_history, save_history, clear_history") ;
74+ return new ErrorResponse ( $ "Unknown action '{ action } '. Valid actions: compile_and_load, list_loaded, get_types, execute_with_roslyn, get_history, save_history, clear_history") ;
7375 }
7476 }
7577
7678 private static object CompileAndLoad ( JObject @params )
7779 {
7880#if ! USE_ROSLYN
79- return Response . Error (
81+ return new ErrorResponse (
8082 "Runtime compilation requires Roslyn. Please install Microsoft.CodeAnalysis.CSharp NuGet package and add USE_ROSLYN to Scripting Define Symbols. " +
8183 "See ManageScript.cs header for installation instructions."
8284 ) ;
8385#else
8486 try
8587 {
8688 string code = @params [ "code" ] ? . ToString ( ) ;
87- var assemblyToken = @params [ "assembly_name" ] ;
88- string assemblyName = assemblyToken == null || string . IsNullOrWhiteSpace ( assemblyToken . ToString ( ) )
89- ? $ "DynamicAssembly_{ DateTime . Now . Ticks } "
90- : assemblyToken . ToString ( ) . Trim ( ) ;
89+ string assemblyName = @params [ "assembly_name" ] ? . ToString ( ) ?? $ "DynamicAssembly_{ DateTime . Now . Ticks } ";
9190 string attachTo = @params [ "attach_to" ] ? . ToString ( ) ;
9291 bool loadImmediately = @params [ "load_immediately" ] ? . ToObject < bool > ( ) ?? true ;
9392
9493 if ( string . IsNullOrEmpty ( code ) )
9594 {
96- return Response . Error ( "'code' parameter is required" ) ;
95+ return new ErrorResponse ( "'code' parameter is required" ) ;
9796 }
9897
9998 // Ensure unique assembly name
@@ -104,21 +103,8 @@ private static object CompileAndLoad(JObject @params)
104103
105104 // Create output directory
106105 Directory . CreateDirectory ( DynamicAssembliesPath ) ;
107- string basePath = Path . GetFullPath ( DynamicAssembliesPath ) ;
108- Directory . CreateDirectory ( basePath ) ;
109- string safeFileName = SanitizeAssemblyFileName ( assemblyName ) ;
110- string dllPath = Path . GetFullPath ( Path . Combine ( basePath , $ "{ safeFileName } .dll") ) ;
111-
112- if ( ! dllPath . StartsWith ( basePath , StringComparison . Ordinal ) )
113- {
114- return Response . Error ( "Assembly name must resolve inside the dynamic assemblies directory." ) ;
115- }
116-
117- if ( File . Exists ( dllPath ) )
118- {
119- dllPath = Path . GetFullPath ( Path . Combine ( basePath , $ "{ safeFileName } _{ DateTime . Now . Ticks } .dll") ) ;
120- }
121-
106+ string dllPath = Path . Combine ( DynamicAssembliesPath , $ "{ assemblyName } .dll") ;
107+
122108 // Parse code
123109 var syntaxTree = CSharpSyntaxTree . ParseText ( code ) ;
124110
@@ -137,7 +123,7 @@ private static object CompileAndLoad(JObject @params)
137123
138124 // Emit to file
139125 EmitResult emitResult ;
140- using ( var stream = new FileStream ( dllPath , FileMode . Create , FileAccess . Write , FileShare . None ) )
126+ using ( var stream = new FileStream ( dllPath , FileMode . Create ) )
141127 {
142128 emitResult = compilation . Emit ( stream ) ;
143129 }
@@ -156,7 +142,7 @@ private static object CompileAndLoad(JObject @params)
156142 } )
157143 . ToList ( ) ;
158144
159- return Response . Error ( "Compilation failed" , new
145+ return new ErrorResponse ( "Compilation failed" , new
160146 {
161147 errors = errors ,
162148 error_count = errors . Count
@@ -222,7 +208,7 @@ private static object CompileAndLoad(JObject @params)
222208 }
223209 }
224210
225- return Response . Success ( "Runtime compilation completed successfully" , new
211+ return new SuccessResponse ( "Runtime compilation completed successfully" , new
226212 {
227213 assembly_name = assemblyName ,
228214 dll_path = dllPath ,
@@ -235,15 +221,15 @@ private static object CompileAndLoad(JObject @params)
235221 }
236222 catch ( Exception ex )
237223 {
238- return Response . Error ( $ "Runtime compilation failed: { ex . Message } ", new
224+ return new ErrorResponse ( $ "Runtime compilation failed: { ex . Message } ", new
239225 {
240226 exception = ex . GetType ( ) . Name ,
241227 stack_trace = ex . StackTrace
242228 } ) ;
243229 }
244230#endif
245231 }
246-
232+
247233 private static object ListLoadedAssemblies ( )
248234 {
249235 var assemblies = LoadedAssemblies . Values . Select ( info => new
@@ -254,33 +240,26 @@ private static object ListLoadedAssemblies()
254240 type_count = info . TypeNames . Count ,
255241 types = info . TypeNames
256242 } ) . ToList ( ) ;
257-
258- return Response . Success ( $ "Found { assemblies . Count } loaded dynamic assemblies", new
243+
244+ return new SuccessResponse ( $ "Found { assemblies . Count } loaded dynamic assemblies", new
259245 {
260246 count = assemblies . Count ,
261247 assemblies = assemblies
262248 } ) ;
263249 }
264250
265- private static string SanitizeAssemblyFileName ( string assemblyName )
266- {
267- var invalidChars = Path . GetInvalidFileNameChars ( ) ;
268- var sanitized = new string ( assemblyName . Where ( c => ! invalidChars . Contains ( c ) ) . ToArray ( ) ) ;
269- return string . IsNullOrWhiteSpace ( sanitized ) ? $ "DynamicAssembly_{ DateTime . Now . Ticks } " : sanitized ;
270- }
271-
272251 private static object GetAssemblyTypes ( JObject @params )
273252 {
274253 string assemblyName = @params [ "assembly_name" ] ? . ToString ( ) ;
275254
276255 if ( string . IsNullOrEmpty ( assemblyName ) )
277256 {
278- return Response . Error ( "'assembly_name' parameter is required" ) ;
257+ return new ErrorResponse ( "'assembly_name' parameter is required" ) ;
279258 }
280259
281260 if ( ! LoadedAssemblies . TryGetValue ( assemblyName , out var info ) )
282261 {
283- return Response . Error ( $ "Assembly '{ assemblyName } ' not found in loaded assemblies") ;
262+ return new ErrorResponse ( $ "Assembly '{ assemblyName } ' not found in loaded assemblies") ;
284263 }
285264
286265 var types = info . Assembly . GetTypes ( ) . Select ( t => new
@@ -294,7 +273,7 @@ private static object GetAssemblyTypes(JObject @params)
294273 base_type = t . BaseType ? . FullName
295274 } ) . ToList ( ) ;
296275
297- return Response . Success ( $ "Retrieved { types . Count } types from { assemblyName } ", new
276+ return new SuccessResponse ( $ "Retrieved { types . Count } types from { assemblyName } ", new
298277 {
299278 assembly_name = assemblyName ,
300279 type_count = types . Count ,
@@ -318,7 +297,7 @@ private static object ExecuteWithRoslyn(JObject @params)
318297
319298 if ( string . IsNullOrEmpty ( code ) )
320299 {
321- return Response . Error ( "'code' parameter is required" ) ;
300+ return new ErrorResponse ( "'code' parameter is required" ) ;
322301 }
323302
324303 // Get or create the RoslynRuntimeCompiler instance
@@ -336,7 +315,7 @@ private static object ExecuteWithRoslyn(JObject @params)
336315
337316 if ( targetObject == null )
338317 {
339- return Response . Error ( $ "Target GameObject '{ targetObjectName } ' not found") ;
318+ return new ErrorResponse ( $ "Target GameObject '{ targetObjectName } ' not found") ;
340319 }
341320 }
342321
@@ -352,7 +331,7 @@ out string errorMessage
352331
353332 if ( success )
354333 {
355- return Response . Success ( $ "Code compiled and executed successfully", new
334+ return new SuccessResponse ( $ "Code compiled and executed successfully", new
356335 {
357336 class_name = className ,
358337 method_name = methodName ,
@@ -363,15 +342,15 @@ out string errorMessage
363342 }
364343 else
365344 {
366- return Response . Error ( $ "Execution failed: { errorMessage } ", new
345+ return new ErrorResponse ( $ "Execution failed: { errorMessage } ", new
367346 {
368347 diagnostics = compiler . lastCompileDiagnostics
369348 } ) ;
370349 }
371350 }
372351 catch ( Exception ex )
373352 {
374- return Response . Error ( $ "Failed to execute with Roslyn: { ex . Message } ", new
353+ return new ErrorResponse ( $ "Failed to execute with Roslyn: { ex . Message } ", new
375354 {
376355 exception = ex . GetType ( ) . Name ,
377356 stack_trace = ex . StackTrace
@@ -402,15 +381,15 @@ private static object GetCompilationHistory()
402381 : entry . sourceCode
403382 } ) . ToList ( ) ;
404383
405- return Response . Success ( $ "Retrieved { historyData . Count } history entries", new
384+ return new SuccessResponse ( $ "Retrieved { historyData . Count } history entries", new
406385 {
407386 count = historyData . Count ,
408387 history = historyData
409388 } ) ;
410389 }
411390 catch ( Exception ex )
412391 {
413- return Response . Error ( $ "Failed to get history: { ex . Message } ") ;
392+ return new ErrorResponse ( $ "Failed to get history: { ex . Message } ") ;
414393 }
415394 }
416395
@@ -425,20 +404,20 @@ private static object SaveCompilationHistory()
425404
426405 if ( compiler . SaveHistoryToFile ( out string savedPath , out string error ) )
427406 {
428- return Response . Success ( $ "History saved successfully", new
407+ return new SuccessResponse ( $ "History saved successfully", new
429408 {
430409 path = savedPath ,
431410 entry_count = compiler . CompilationHistory . Count
432411 } ) ;
433412 }
434413 else
435414 {
436- return Response . Error ( $ "Failed to save history: { error } ") ;
415+ return new ErrorResponse ( $ "Failed to save history: { error } ") ;
437416 }
438417 }
439418 catch ( Exception ex )
440419 {
441- return Response . Error ( $ "Failed to save history: { ex . Message } ") ;
420+ return new ErrorResponse ( $ "Failed to save history: { ex . Message } ") ;
442421 }
443422 }
444423
@@ -453,11 +432,11 @@ private static object ClearCompilationHistory()
453432 int count = compiler . CompilationHistory . Count ;
454433 compiler . ClearHistory ( ) ;
455434
456- return Response . Success ( $ "Cleared { count } history entries") ;
435+ return new SuccessResponse ( $ "Cleared { count } history entries") ;
457436 }
458437 catch ( Exception ex )
459438 {
460- return Response . Error ( $ "Failed to clear history: { ex . Message } ") ;
439+ return new ErrorResponse ( $ "Failed to clear history: { ex . Message } ") ;
461440 }
462441 }
463442
0 commit comments