| name | executing-csharp-scripts-in-unity-editor |
| description | Use this skill always when you need to use execute_csharp_script_in_unity_editor tool to modify scenes, add, remove or modify game objects, components, scriptable objects or perform any other task in Unity Editor using C# scripts. Also covers when and how to use read_unity_console_logs and run_unity_tests tools as part of the same workflow and create or modify favourite scripts used to automate tasks. |
Executing C# Scripts in Unity Editor
Table of Contents
- Available Tools
- Core Principles
- Forbidden Patterns
- Usage Workflow
- Debugging Loop
- Favourite Scripts
- Script Context and APIs
- Common Scripting Patterns
Available Tools
This skill coordinates three tools within one workflow. Use the tool that matches the task, and combine them when the task crosses from source edits to Editor automation or test verification.
| Tool | Purpose | When to Use |
|---|
execute_csharp_script_in_unity_editor | Executes C# code in the live Unity Editor (Roslyn) | Modifying scenes, GameObjects, Components, Prefabs, ScriptableObjects, or running Editor automation. Script output and logs are returned directly in the tool result. |
read_unity_console_logs | Reads recent entries from the Unity Editor Console | Before executing an Editor script or Unity test when the task depends on compiled C# code. Also useful when debugging unexpected Editor state. |
run_unity_tests | Runs EditMode or PlayMode Unity tests via TestRunnerApi | After editing C# source files to verify logic correctness. Do not use it for ad-hoc code execution, and do not run it solely because an Editor script was executed. |
Execute C# Script Tool Contract
Use execute_csharp_script_in_unity_editor for live Unity Editor work:
- Modify scene objects, GameObjects, Components, Transforms, UI elements, Prefabs, ScriptableObjects, or other assets through Unity APIs.
- Query current Editor, scene, prefab, or asset state, such as listing objects or reading component values.
- Batch-process or automate Editor tasks.
- Compute values with Unity math and physics APIs such as
Mathf, Vector3, Quaternion, and Physics when the result depends on Unity behavior.
Do not use execute_csharp_script_in_unity_editor for these tasks:
- Editing C# source files. Use file editing tools.
- Reading or writing plain text, JSON, YAML, or other project files. Use file tools.
- Installing packages or changing ProjectSettings. Use dedicated project tooling or a narrower explicit workflow.
- Running Unity tests. Use
run_unity_tests.
The tool runs raw C# top-level statements in the Unity Editor with Roslyn. It executes synchronously on the main thread, has access to the loaded Unity/project assemblies, captures Debug.Log(), Debug.LogError(), and the final evaluated expression, and marks the active scene dirty after successful execution in edit mode.
Core Principles
- Top-Level Statements Only: Write flat code. Do not wrap code in a class, method, or
[MenuItem]. Provide only the sequence of statements and any required using directives.
- Pre-Imported Namespaces:
System, System.Collections.Generic, System.Linq, UnityEngine, UnityEditor are always available. Do not redeclare them with using.
- Explicit Usings for Others: Declare any namespace not in the pre-imported list (e.g.,
using UnityEngine.UI;).
- Idempotency: Scripts must be safe to rerun. Check for existence before creating or adding to avoid duplicates. Use helper patterns like
GetOrAddComponent and CreateOrGetGameObject.
- Synchronous Only: Do not use
async/await, Task, or Task.Run. All Unity Editor APIs are main-thread-only and synchronous.
- Null Checks: Always use
== null for Unity objects. Do not use ?? or ?. — Unity objects override the == operator in ways that break null-conditional operators.
- Specificity: Prefer fully qualified names (e.g.,
UnityEngine.GameObject, UnityEditor.AssetDatabase) to avoid ambiguity.
- Script Feedback: Use
UnityEngine.Debug.Log() to report what the script did. Use UnityEngine.Debug.LogError() for failures. The tool captures both.
- Clarity & Error Handling: Comment non-obvious logic. Wrap risky operations in
try-catch and log errors with context.
- Object class ambiguity: Always use
UnityEngine.Object when referring to Unity objects to avoid confusion with System.Object.
Forbidden Patterns
CRITICAL: Never simulate an action or fabricate output if the API is unavailable.
- Do NOT log messages claiming to "simulate" or "pretend" a modification was made.
- Do NOT create placeholder logic that does nothing and logs fake success.
- Do NOT use
execute_csharp_script_in_unity_editor to create or edit C# source files — use file editing tools instead.
- Do NOT use
execute_csharp_script_in_unity_editor to read/write plain text, JSON, or YAML files — use file tools instead.
- Do NOT use
execute_csharp_script_in_unity_editor to run tests — use run_unity_tests instead.
- Do NOT use background threads (
Thread, Task.Run) — all Unity Editor APIs require the main thread.
- Do NOT use
async/await inside scripts.
Usage Workflow
Use the applicable steps in this order for each task. Skip only the steps that do not apply.
- Analyze Requirements: Understand what needs to be created, modified, or queried. Identify target GameObjects, Components, assets, source files, and expected outcome.
- Check for Compilation Errors: If the task depends on compiled C# code, call
read_unity_console_logs (max_entries: 20) before executing any Editor script or Unity test. Prior C# file edits may have broken the build. Do not proceed until compilation errors are resolved.
- Analyze Unity Context: When the task needs scene, prefab, or asset inspection, use
execute_csharp_script_in_unity_editor to inspect hierarchy, existing components, and current asset state. Identify edge cases such as missing objects, duplicate components, or invalid asset paths.
- Plan the Action: Determine which Unity APIs are needed and whether the task requires Editor scripting, source-file edits, Unity tests, or some combination of them.
- Write the Script or Source Change: Apply all Core Principles. For Editor scripts, add idempotency guards, null checks,
Debug.Log for significant actions, and try-catch for risky operations.
- Execute the Script: When using Editor automation, call
execute_csharp_script_in_unity_editor. All Debug.Log and Debug.LogError output is returned directly in the tool result — no separate log read is needed.
- Run Tests When Source Changed: If you edited compiled C# source files, run the narrowest relevant Unity tests with
run_unity_tests after the code compiles cleanly.
- Fix and Retry: If script execution, compilation, or tests fail, apply the Debugging Loop before reporting completion.
Debugging Loop
When a script execution returns errors or unexpected results:
- Read the error message and stack trace returned directly in the tool result.
- If the error suggests a compilation failure (e.g., type not found, missing member), call
read_unity_console_logs (max_entries: 30) to identify the source C# file causing the build error, fix it, then re-execute.
- Otherwise, identify the root cause from the script output: missing object, bad asset path, wrong API usage, or null reference.
- Fix the script and re-execute.
- Repeat until the tool result confirms success with no errors.
Favourite Scripts
Favourite Scripts are reusable C# scripts stored in .unityCodeMcpServer/favouriteScripts.json at the project root. They share the same execution engine as execute_csharp_script_in_unity_editor and are accessible from the Unity Editor at Tools > UnityCodeMcpServer > Favourite Scripts.
Use Favourite Scripts for any automation that is likely to be run again — bulk asset updates, scene wiring, import fixes, procedural placement. Once saved, the script is a one-click tool.
File Location and Format
{project root}/.unityCodeMcpServer/favouriteScripts.json
[
{
"name": "Script Name",
"script": "// C# top-level statements — same rules as execute_csharp_script_in_unity_editor"
}
]
Saving a Favourite Script
When the user asks to save a script as a favourite, use file tools to read the current JSON, upsert the entry (match by name, case-insensitive), then write it back.
1. Read `.unityCodeMcpServer/favouriteScripts.json` (create as empty array `[]` if missing).
2. Find an existing entry whose `name` matches — replace it. Otherwise append.
3. Write the updated array back with indentation.
Always preserve all existing entries when writing back. Never overwrite the file with only the new entry.
Updating and Deleting
- Update: same upsert flow — match by name, replace the
script value.
- Delete: remove the matching entry and write the array back.
Workflow
- Write and refine the script using
execute_csharp_script_in_unity_editor until the output is correct.
- When the user is satisfied, save it as a favourite. The user can then run it any time from the Editor window without the agent.
Script Context and APIs
Pre-Imported Namespaces (do NOT add using for these)
System
System.Collections.Generic
System.Linq
UnityEngine
UnityEditor
Commonly Needed Explicit Usings
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using TMPro;
Loaded Assemblies
Only loaded assemblies are available.
Assemblies included by default:
- System.Core,
- UnityEditor.CoreModule,
- UnityEngine.CoreModule,
- Assembly-CSharp,
- Assembly-CSharp-Editor
- UnityCodeMcpServer
Loading Additional Assemblies:
Additional assemblies are defined in AdditionalAssemblyNames list in Assets/Plugins/UnityCodeAgent/Editor/UnityCodeAgentSettings.asset. Add any assembly name there (e.g., "MyCustomAssembly") and it will be loaded and available in the script context.
When encountering errors about missing types or namespaces, like error CS0234: The type or namespace name 'UI' does not exist in the namespace 'UnityEngine' (are you missing an assembly reference?):
- Identify the required assembly and namespace for the API you are trying to use.
- Use the following code to add an assembly to the settings:
UnityCodeMcpServer.Settings.UnityCodeMcpServerSettings.Instance.AddAssembly("UnityEngine.Physics2DModule");
Key API Reference
| Task | API |
|---|
| Find active scene GameObject | UnityEngine.GameObject.Find("Name") |
| Find all objects of type | UnityEngine.Object.FindObjectsByType<T>(FindObjectsSortMode.None) |
| Load asset from path | UnityEditor.AssetDatabase.LoadAssetAtPath<T>("Assets/...") |
| Save all dirty assets | UnityEditor.AssetDatabase.SaveAssets() |
| Refresh asset database | UnityEditor.AssetDatabase.Refresh() |
| Mark object dirty | UnityEditor.EditorUtility.SetDirty(obj) |
| Open scene | UnityEditor.SceneManagement.EditorSceneManager.OpenScene("Assets/Scenes/MyScene.unity") |
| Save open scene | UnityEditor.SceneManagement.EditorSceneManager.SaveOpenScenes() |
Common Scripting Patterns
Safe Get-or-Add Component
T GetOrAddComponent<T>(UnityEngine.GameObject go) where T : UnityEngine.Component {
var c = go.GetComponent<T>();
if (c == null) c = go.AddComponent<T>();
return c;
}
Safe Create-or-Get GameObject
UnityEngine.GameObject CreateOrGetGameObject(string name, UnityEngine.Transform parent = null) {
var existing = UnityEngine.GameObject.Find(name);
if (existing != null) return existing;
var go = new UnityEngine.GameObject(name);
if (parent != null) go.transform.SetParent(parent, false);
return go;
}
Creating and Parenting Objects
var parent = UnityEngine.GameObject.Find("Canvas");
if (parent == null) { UnityEngine.Debug.LogError("Canvas not found"); return; }
var panel = CreateOrGetGameObject("HUD_Panel", parent.transform);
using UnityEngine.UI;
var image = GetOrAddComponent<Image>(panel);
image.color = new UnityEngine.Color(0f, 0f, 0f, 0.5f);
UnityEngine.Debug.Log("HUD_Panel configured");
Modifying Prefab Assets
CRITICAL: Always use the Load → Modify → Save → Unload cycle. Never edit a prefab in-scene to affect the asset.
string path = "Assets/Prefabs/MyPrefab.prefab";
var root = UnityEditor.PrefabUtility.LoadPrefabContents(path);
if (root == null) { UnityEngine.Debug.LogError("Prefab not found: " + path); return; }
try {
var rb = GetOrAddComponent<UnityEngine.Rigidbody2D>(root);
rb.gravityScale = 0f;
UnityEditor.PrefabUtility.SaveAsPrefabAsset(root, path);
UnityEngine.Debug.Log("Prefab saved: " + path);
} finally {
UnityEditor.PrefabUtility.UnloadPrefabContents(root);
}
Querying Scene State
var allObjects = UnityEngine.Object.FindObjectsByType<UnityEngine.GameObject>(UnityEngine.FindObjectsSortMode.None);
foreach (var go in allObjects) {
UnityEngine.Debug.Log($"{go.name} | Layer: {UnityEngine.LayerMask.LayerToName(go.layer)} | Active: {go.activeSelf}");
}
Creating a ScriptableObject Asset
var so = UnityEngine.ScriptableObject.CreateInstance<MyScriptableObjectType>();
so.someField = 42;
UnityEditor.AssetDatabase.CreateAsset(so, "Assets/Data/MyAsset.asset");
UnityEditor.AssetDatabase.SaveAssets();
UnityEngine.Debug.Log("ScriptableObject created at Assets/Data/MyAsset.asset");
Deleting a GameObject
Store the name before destroying, since the object becomes invalid immediately after.
var target = UnityEngine.GameObject.Find("ToDestroy");
if (target != null) {
string name = target.name;
UnityEngine.Object.DestroyImmediate(target);
UnityEngine.Debug.Log("Destroyed: " + name);
} else {
UnityEngine.Debug.Log("Object not found — nothing to destroy");
}
Missing Namespace or Assembly Errors
When encountering errors about missing types or namespaces, like error CS0234: The type or namespace name 'UI' does not exist in the namespace 'UnityEngine' (are you missing an assembly reference?):
- Identify the required assembly and namespace for the API you are trying to use.
- Check if that assembly is included in the loaded assemblies or
AdditionalAssemblyNames in UnityCodeMcpServerSettings.asset.
- If it is not, add it to
AdditionalAssemblyNames in UnityCodeMcpServerSettings.asset via file tools.
- Force a settings reload by executing the reload script from the Forcing Settings Reload After File Edit section above. The new assembly will not be available until this is done.
- Ensure you have the correct
using directive for the namespace at the top of your script.