unity-mcp-skill/references/workflows.md
Common workflows and patterns for effective Unity-MCP usage.
# 1. Check editor state
# Read mcpforunity://editor/state
# 2. Verify ready_for_tools == true
# If false, wait for recommended_retry_after_ms
# 3. Check active scene
# Read mcpforunity://editor/state → active_scene
# 4. List available instances (multi-instance)
# Read mcpforunity://instances
# Quick readiness check pattern:
editor_state = read_resource("mcpforunity://editor/state")
if not editor_state["ready_for_tools"]:
# Check blocking_reasons
# Wait recommended_retry_after_ms
pass
if editor_state["is_compiling"]:
# Wait for compilation to complete
pass
Always start a generated scene build with manage_scene(action="create") to get a clean empty scene. This avoids conflicts with existing default objects (Camera, Light) that would cause "already exists" errors when the execution plan tries to create its own.
# Step 0: Create fresh empty scene (replaces current scene entirely)
manage_scene(action="create", name="MyGeneratedScene", path="Assets/Scenes/")
# Now proceed with the phased execution plan...
# Phase 1: Environment (camera, lights) — no conflicts
# Phase 2: Objects (GameObjects)
# Phase 3: Materials
# etc.
After creating scripts and attaching components, use set_property to wire cross-references between GameObjects. Use the {"name": "ObjectName"} format to reference scene objects by name:
# Wire a list of target GameObjects into a script's serialized field
manage_components(
action="set_property",
target="BeeManager",
component_type="BeeManagerScript",
property="targetObjects",
value=[{"name": "Flower_1"}, {"name": "Flower_2"}, {"name": "Flower_3"}]
)
When scripts use OnTriggerEnter / OnTriggerStay / OnTriggerExit, at least one of the two colliding objects must have a Rigidbody component. Common pattern:
# Moving objects (bees, players) need Rigidbody for triggers to fire
batch_execute(commands=[
{"tool": "manage_components", "params": {
"action": "add", "target": "Bee_1", "component_type": "Rigidbody"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "Bee_1",
"component_type": "Rigidbody",
"properties": {"useGravity": false, "isKinematic": true}
}}
])
manage_script(action="update")When a generated script needs to be rewritten (e.g., to add auto-wiring logic), use update instead of deleting and recreating:
manage_script(
action="update",
path="Assets/Scripts/MyScript.cs",
contents="using UnityEngine;\n\npublic class MyScript : MonoBehaviour { ... }"
)
# manage_script update auto-triggers import + compile — just wait and check console
# Read mcpforunity://editor/state → wait until is_compiling == false
read_console(types=["error"], count=10)
# 1. Create new scene
manage_scene(action="create", name="GameLevel", path="Assets/Scenes/")
# 2. Batch create environment objects
batch_execute(commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "Ground", "primitive_type": "Plane",
"position": [0, 0, 0], "scale": [10, 1, 10]
}},
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "Light", "primitive_type": "Cube"
}},
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "Player", "primitive_type": "Capsule",
"position": [0, 1, 0]
}}
])
# 3. Add light component (delete cube mesh, add light)
manage_components(action="remove", target="Light", component_type="MeshRenderer")
manage_components(action="remove", target="Light", component_type="MeshFilter")
manage_components(action="remove", target="Light", component_type="BoxCollider")
manage_components(action="add", target="Light", component_type="Light")
manage_components(action="set_property", target="Light", component_type="Light",
property="type", value="Directional")
# 4. Set up camera
manage_gameobject(action="modify", target="Main Camera", position=[0, 5, -10],
rotation=[30, 0, 0])
# 5. Verify with screenshot
manage_camera(action="screenshot")
# 6. Save scene
manage_scene(action="save")
# Create 5x5 grid of cubes using batch
commands = []
for x in range(5):
for z in range(5):
commands.append({
"tool": "manage_gameobject",
"params": {
"action": "create",
"name": f"Cube_{x}_{z}",
"primitive_type": "Cube",
"position": [x * 2, 0, z * 2]
}
})
# Execute in batches of 25
batch_execute(commands=commands[:25], parallel=True)
# Find template object
result = find_gameobjects(search_term="Template", search_method="by_name")
template_id = result["ids"][0]
# Duplicate in a line
for i in range(10):
manage_gameobject(
action="duplicate",
target=template_id,
new_name=f"Instance_{i}",
offset=[i * 2, 0, 0]
)
# 1. Create script (automatically triggers import + compilation)
create_script(
path="Assets/Scripts/EnemyAI.cs",
contents='''using UnityEngine;
public class EnemyAI : MonoBehaviour
{
public float speed = 5f;
public Transform target;
void Update()
{
if (target != null)
{
Vector3 direction = (target.position - transform.position).normalized;
transform.position += direction * speed * Time.deltaTime;
}
}
}'''
)
# 2. Wait for compilation to finish
# Read mcpforunity://editor/state → wait until is_compiling == false
# 3. Check for errors
console = read_console(types=["error"], count=10)
if console["messages"]:
# Handle compilation errors
print("Compilation errors:", console["messages"])
else:
# 4. Attach to GameObject
manage_gameobject(action="modify", target="Enemy", components_to_add=["EnemyAI"])
# 5. Set component properties
manage_components(
action="set_property",
target="Enemy",
component_type="EnemyAI",
properties={
"speed": 10.0
}
)
# 1. Get current SHA
sha_info = get_sha(uri="mcpforunity://path/Assets/Scripts/PlayerController.cs")
# 2. Find the method to edit
matches = find_in_file(
uri="mcpforunity://path/Assets/Scripts/PlayerController.cs",
pattern="void Update\\(\\)"
)
# 3. Apply structured edit
script_apply_edits(
name="PlayerController",
path="Assets/Scripts",
edits=[{
"op": "replace_method",
"methodName": "Update",
"replacement": '''void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
transform.Translate(new Vector3(h, 0, v) * speed * Time.deltaTime);
}'''
}]
)
# 4. Validate
validate_script(
uri="mcpforunity://path/Assets/Scripts/PlayerController.cs",
level="standard"
)
# 5. Wait for compilation (script_apply_edits auto-triggers import + compile)
# Read mcpforunity://editor/state → wait until is_compiling == false
# 6. Check console
read_console(types=["error"], count=10)
script_apply_edits(
name="GameManager",
path="Assets/Scripts",
edits=[
{
"op": "insert_method",
"afterMethod": "Start",
"code": '''
public void ResetGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}'''
},
{
"op": "anchor_insert",
"anchor": "using UnityEngine;",
"position": "after",
"text": "\nusing UnityEngine.SceneManagement;"
}
]
)
# 1. Create material
manage_material(
action="create",
material_path="Assets/Materials/PlayerMaterial.mat",
shader="Standard",
properties={
"_Color": [0.2, 0.5, 1.0, 1.0],
"_Metallic": 0.5,
"_Glossiness": 0.8
}
)
# 2. Assign to renderer
manage_material(
action="assign_material_to_renderer",
target="Player",
material_path="Assets/Materials/PlayerMaterial.mat",
slot=0
)
# 3. Verify visually
manage_camera(action="screenshot")
# 1. Create base texture
manage_texture(
action="create",
path="Assets/Textures/Checkerboard.png",
width=256,
height=256,
fill_color=[255, 255, 255, 255]
)
# 2. Apply checkerboard pattern
manage_texture(
action="apply_pattern",
path="Assets/Textures/Checkerboard.png",
pattern="checkerboard",
palette=[[0, 0, 0, 255], [255, 255, 255, 255]],
pattern_size=32
)
# 3. Create material with texture
manage_material(
action="create",
material_path="Assets/Materials/CheckerMaterial.mat",
shader="Standard"
)
# 4. Assign texture to material (via manage_material set_material_shader_property)
# 1. Create folder structure
batch_execute(commands=[
{"tool": "manage_asset", "params": {"action": "create_folder", "path": "Assets/Prefabs"}},
{"tool": "manage_asset", "params": {"action": "create_folder", "path": "Assets/Materials"}},
{"tool": "manage_asset", "params": {"action": "create_folder", "path": "Assets/Scripts"}},
{"tool": "manage_asset", "params": {"action": "create_folder", "path": "Assets/Textures"}}
])
# 2. Move existing assets
manage_asset(action="move", path="Assets/MyMaterial.mat", destination="Assets/Materials/MyMaterial.mat")
manage_asset(action="move", path="Assets/MyScript.cs", destination="Assets/Scripts/MyScript.cs")
# Find all prefabs
result = manage_asset(
action="search",
path="Assets",
search_pattern="*.prefab",
page_size=50,
generate_preview=False
)
# Process each prefab
for asset in result["assets"]:
prefab_path = asset["path"]
# Get prefab info
info = manage_prefabs(action="get_info", prefab_path=prefab_path)
print(f"Prefab: {prefab_path}, Children: {info['childCount']}")
Use manage_gameobject (not manage_prefabs) to place prefab instances in the scene.
# Full path
manage_gameobject(
action="create",
name="Enemy_1",
prefab_path="Assets/Prefabs/Enemy.prefab",
position=[5, 0, 3],
parent="Enemies"
)
# Smart lookup — just the prefab name works too
manage_gameobject(action="create", name="Enemy_2", prefab_path="Enemy", position=[10, 0, 3])
# Batch-spawn multiple instances
batch_execute(commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": f"Enemy_{i}",
"prefab_path": "Enemy", "position": [i * 3, 0, 0], "parent": "Enemies"
}}
for i in range(5)
])
Note:
manage_prefabsis for headless prefab editing (inspect, modify contents, create from GameObject). To instantiate a prefab into the scene, always usemanage_gameobject(action="create", prefab_path="...").
# 1. List available tests
# Read mcpforunity://tests/EditMode
# 2. Run specific tests
result = run_tests(
mode="EditMode",
test_names=["MyTests.TestPlayerMovement", "MyTests.TestEnemySpawn"],
include_failed_tests=True
)
job_id = result["job_id"]
# 3. Wait for results
final_result = get_test_job(
job_id=job_id,
wait_timeout=60,
include_failed_tests=True
)
# 4. Check results
if final_result["status"] == "complete":
for test in final_result.get("failed_tests", []):
print(f"FAILED: {test['name']}: {test['message']}")
# Run all unit tests
result = run_tests(
mode="EditMode",
category_names=["Unit"],
include_failed_tests=True
)
# Poll until complete
while True:
status = get_test_job(job_id=result["job_id"], wait_timeout=30)
if status["status"] in ["complete", "failed"]:
break
# 1. Write test first
create_script(
path="Assets/Tests/Editor/PlayerTests.cs",
contents='''using NUnit.Framework;
using UnityEngine;
public class PlayerTests
{
[Test]
public void TestPlayerStartsAtOrigin()
{
var player = new GameObject("TestPlayer");
Assert.AreEqual(Vector3.zero, player.transform.position);
Object.DestroyImmediate(player);
}
}'''
)
# 2. Wait for compilation (create_script auto-triggers import + compile)
# Read mcpforunity://editor/state → wait until is_compiling == false
# 3. Run test (expect pass for this simple test)
result = run_tests(mode="EditMode", test_names=["PlayerTests.TestPlayerStartsAtOrigin"])
get_test_job(job_id=result["job_id"], wait_timeout=30)
# 1. Check console for errors
errors = read_console(
types=["error"],
count=20,
include_stacktrace=True,
format="detailed"
)
# 2. For each error, find the file and line
for error in errors["messages"]:
# Parse error message for file:line info
# Use find_in_file to locate the problematic code
pass
# 3. After fixing, refresh and check again
refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True)
read_console(types=["error"], count=10)
# 1. Find the GameObject
result = find_gameobjects(search_term="Player", search_method="by_name")
# 2. Get all components
# Read mcpforunity://scene/gameobject/{id}/components
# 3. Check for null references in serialized fields
# Look for fields with null/missing values
# 4. Find the referenced object
result = find_gameobjects(search_term="Target", search_method="by_name")
# 5. Set the reference
manage_components(
action="set_property",
target="Player",
component_type="PlayerController",
property="target",
value={"instanceID": result["ids"][0]} # Reference by ID
)
# 1. Get hierarchy
hierarchy = manage_scene(action="get_hierarchy", page_size=100, include_transform=True)
# 2. Find objects at unexpected positions
for item in hierarchy["data"]["items"]:
if item.get("transform", {}).get("position", [0,0,0])[1] < -100:
print(f"Object {item['name']} fell through floor!")
# 3. Visual verification
manage_camera(action="screenshot")
Unity has two UI systems: UI Toolkit (modern, recommended) and uGUI (Canvas-based, legacy). Use manage_ui for UI Toolkit workflows, and batch_execute with manage_gameobject + manage_components for uGUI.
Template warning: This section is a skill template library, not a guaranteed source of truth. Examples may be inaccurate for your Unity version, package setup, or project conventions. Use safely:
- Always read
mcpforunity://project/infofirst to detect installed packages and input system.- Validate component/property names against the current project.
- Prefer targeting by instance ID or full path over generic names.
- Treat numeric enum values as placeholders and verify before reuse.
Before creating any UI, read project info to determine which packages and input system are available.
# Read mcpforunity://project/info — returns:
# {
# "renderPipeline": "BuiltIn" | "Universal" | "HighDefinition" | "Custom",
# "activeInputHandler": "Old" | "New" | "Both",
# "packages": {
# "ugui": true/false, — com.unity.ugui (Canvas, Image, Button, etc.)
# "textmeshpro": true/false, — com.unity.textmeshpro (TextMeshProUGUI)
# "inputsystem": true/false, — com.unity.inputsystem (new Input System)
# "uiToolkit": true/false, — UI Toolkit (always true for Unity 2021.3+)
# "screenCapture": true/false — ScreenCapture module enabled
# }
# }
Decision matrix:
| project_info field | Value | What to use |
|---|---|---|
packages.uiToolkit | true | Preferred: Use manage_ui for UI Toolkit (UXML/USS) |
packages.ugui | true | Canvas-based UI (Image, Button, etc.) via batch_execute |
packages.textmeshpro | true | TextMeshProUGUI for text (uGUI) |
packages.textmeshpro | false | UnityEngine.UI.Text (legacy, lower quality) |
activeInputHandler | "Old" | StandaloneInputModule for EventSystem (uGUI) |
activeInputHandler | "New" | InputSystemUIInputModule for EventSystem (uGUI) |
activeInputHandler | "Both" | Either works; prefer InputSystemUIInputModule for UI |
UI Toolkit uses a web-like approach: UXML (like HTML) for structure, USS (like CSS) for styling. This is the preferred UI system for new projects.
Important: Always use
<ui:Style>(with theui:namespace prefix) in UXML, not bare<Style>. UI Builder will fail to open files that use<Style>without the prefix.
# 1. Create UXML document (structure)
manage_ui(
action="create",
path="Assets/UI/MainMenu.uxml",
contents='''<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements">
<ui:Style src="Assets/UI/MainMenu.uss" />
<ui:VisualElement name="root" class="root-container">
<ui:Label text="My Game" class="title" />
<ui:Button text="Play" name="play-btn" class="menu-button" />
<ui:Button text="Settings" name="settings-btn" class="menu-button" />
<ui:Button text="Quit" name="quit-btn" class="menu-button" />
</ui:VisualElement>
</ui:UXML>'''
)
# 2. Create USS stylesheet (styling)
manage_ui(
action="create",
path="Assets/UI/MainMenu.uss",
contents='''.root-container {
flex-grow: 1;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.8);
}
.title {
font-size: 48px;
color: white;
-unity-font-style: bold;
margin-bottom: 40px;
}
.menu-button {
width: 300px;
height: 60px;
font-size: 24px;
margin: 8px;
background-color: rgb(50, 120, 200);
color: white;
border-radius: 8px;
}
.menu-button:hover {
background-color: rgb(70, 140, 220);
}'''
)
# 3. Create a GameObject and attach UIDocument
manage_gameobject(action="create", name="UIRoot")
manage_ui(
action="attach_ui_document",
target="UIRoot",
source_asset="Assets/UI/MainMenu.uxml"
# panel_settings auto-created if omitted
)
# 4. Verify the visual tree
manage_ui(action="get_visual_tree", target="UIRoot", max_depth=5)
# Read current content
result = manage_ui(action="read", path="Assets/UI/MainMenu.uss")
# Modify and update
manage_ui(
action="update",
path="Assets/UI/MainMenu.uss",
contents=".title { font-size: 64px; color: yellow; }"
)
# Create PanelSettings with ScaleWithScreenSize
manage_ui(
action="create_panel_settings",
path="Assets/UI/GamePanelSettings.asset",
scale_mode="ScaleWithScreenSize",
reference_resolution={"width": 1920, "height": 1080}
)
# Attach UIDocument with custom PanelSettings
manage_ui(
action="attach_ui_document",
target="UIRoot",
source_asset="Assets/UI/MainMenu.uxml",
panel_settings="Assets/UI/GamePanelSettings.asset"
)
The sections below cover legacy Canvas-based UI using batch_execute. Use these when working with existing uGUI projects or when UI Toolkit is not suitable.
Every GameObject under a Canvas gets a RectTransform instead of Transform. Without setting anchor/size, UI elements default to zero size and won't be visible. Use set_property on RectTransform:
# Stretch to fill parent (common for panels/backgrounds)
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MyPanel",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0, 0], # bottom-left corner
"anchorMax": [1, 1], # top-right corner
"sizeDelta": [0, 0], # no extra size beyond anchors
"anchoredPosition": [0, 0] # centered on anchors
}
}}
# Fixed-size centered element (e.g. 300x50 button)
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MyButton",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0.5, 0.5],
"anchorMax": [0.5, 0.5],
"sizeDelta": [300, 50],
"anchoredPosition": [0, 0]
}
}}
# Top-anchored bar (e.g. health bar at top of screen)
{"tool": "manage_components", "params": {
"action": "set_property", "target": "TopBar",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0, 1], # left-top
"anchorMax": [1, 1], # right-top (stretch horizontally)
"sizeDelta": [0, 60], # 60px tall, full width
"anchoredPosition": [0, -30] # offset down by half height
}
}}
Note: Vector2 properties accept both
[x, y]array format and{"x": ..., "y": ...}object format.
Every UI element must be under a Canvas. A Canvas requires three components: Canvas, CanvasScaler, and GraphicRaycaster.
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "MainCanvas"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "MainCanvas", "component_type": "Canvas"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "MainCanvas", "component_type": "CanvasScaler"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "MainCanvas", "component_type": "GraphicRaycaster"
}},
# renderMode: 0=ScreenSpaceOverlay, 1=ScreenSpaceCamera, 2=WorldSpace
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MainCanvas",
"component_type": "Canvas", "property": "renderMode", "value": 0
}},
# CanvasScaler: uiScaleMode 1=ScaleWithScreenSize
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MainCanvas",
"component_type": "CanvasScaler", "property": "uiScaleMode", "value": 1
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MainCanvas",
"component_type": "CanvasScaler", "property": "referenceResolution",
"value": [1920, 1080]
}}
])
If no EventSystem exists in the scene, buttons and other interactive UI elements won't respond to input. Create one alongside your first Canvas. Check project_info.activeInputHandler to pick the correct input module.
# For activeInputHandler == "New" or "Both" (project has Input System package):
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "EventSystem"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.EventSystems.EventSystem"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.InputSystem.UI.InputSystemUIInputModule"
}}
])
# For activeInputHandler == "Old" (legacy Input Manager only):
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "EventSystem"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.EventSystems.EventSystem"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.EventSystems.StandaloneInputModule"
}}
])
A Panel is an Image component used as a background/container for other UI elements.
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "MenuPanel", "parent": "MainCanvas"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "MenuPanel", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MenuPanel",
"component_type": "Image", "property": "color",
"value": [0.1, 0.1, 0.1, 0.8]
}},
# Size the panel (stretch to 60% of canvas, centered)
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MenuPanel",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0.2, 0.1], "anchorMax": [0.8, 0.9],
"sizeDelta": [0, 0], "anchoredPosition": [0, 0]
}
}}
])
TextMeshProUGUI automatically adds a RectTransform when added to a child of a Canvas. If packages.textmeshpro is false, use UnityEngine.UI.Text instead.
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "TitleText", "parent": "MenuPanel"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "TitleText",
"component_type": "TextMeshProUGUI"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "TitleText",
"component_type": "TextMeshProUGUI",
"properties": {
"text": "My Game Title",
"fontSize": 48,
"alignment": 514,
"color": [1, 1, 1, 1]
}
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "TitleText",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0, 0.8], "anchorMax": [1, 1],
"sizeDelta": [0, 0], "anchoredPosition": [0, 0]
}
}}
])
TextMeshPro alignment values: 257=TopLeft, 258=TopCenter, 260=TopRight, 513=MiddleLeft, 514=MiddleCenter, 516=MiddleRight, 1025=BottomLeft, 1026=BottomCenter, 1028=BottomRight.
A Button needs an Image (visual) + Button (interaction) on the parent, and a child with TextMeshProUGUI for the label.
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "StartButton", "parent": "MenuPanel"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "StartButton", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "StartButton", "component_type": "Button"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "StartButton",
"component_type": "Image", "property": "color",
"value": [0.2, 0.6, 1.0, 1.0]
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "StartButton",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0.5, 0.5], "anchorMax": [0.5, 0.5],
"sizeDelta": [300, 60], "anchoredPosition": [0, 0]
}
}},
# Child text label (stretches to fill button)
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "StartButton_Label", "parent": "StartButton"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "StartButton_Label",
"component_type": "TextMeshProUGUI"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "StartButton_Label",
"component_type": "TextMeshProUGUI",
"properties": {"text": "Start Game", "fontSize": 24, "alignment": 514}
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "StartButton_Label",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0, 0], "anchorMax": [1, 1],
"sizeDelta": [0, 0], "anchoredPosition": [0, 0]
}
}}
])
A Slider requires a specific hierarchy and must have its fillRect and handleRect references wired to function.
# Step 1: Create hierarchy
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "HealthSlider", "parent": "MainCanvas"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "HealthSlider", "component_type": "Slider"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "HealthSlider", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "HealthSlider",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0.5, 0.5], "anchorMax": [0.5, 0.5],
"sizeDelta": [400, 30], "anchoredPosition": [0, 0]
}
}},
# Background
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "SliderBG", "parent": "HealthSlider"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "SliderBG", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "SliderBG",
"component_type": "Image", "property": "color", "value": [0.3, 0.3, 0.3, 1.0]
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "SliderBG",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}
}},
# Fill Area + Fill
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "FillArea", "parent": "HealthSlider"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "FillArea",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}
}},
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "SliderFill", "parent": "FillArea"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "SliderFill", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "SliderFill",
"component_type": "Image", "property": "color", "value": [0.2, 0.8, 0.2, 1.0]
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "SliderFill",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}
}},
# Handle Area + Handle
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "HandleArea", "parent": "HealthSlider"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "HandleArea",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}
}},
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "SliderHandle", "parent": "HandleArea"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "SliderHandle", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "SliderHandle",
"component_type": "RectTransform",
"properties": {"anchorMin": [0.5, 0], "anchorMax": [0.5, 1], "sizeDelta": [20, 0]}
}}
])
# Step 2: Wire Slider references (CRITICAL — slider won't work without this)
batch_execute(fail_fast=True, commands=[
{"tool": "manage_components", "params": {
"action": "set_property", "target": "HealthSlider",
"component_type": "Slider", "property": "fillRect",
"value": {"name": "SliderFill"}
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "HealthSlider",
"component_type": "Slider", "property": "handleRect",
"value": {"name": "SliderHandle"}
}}
])
# Step 1: Create hierarchy
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "NameInput", "parent": "MenuPanel"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "NameInput", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "NameInput",
"component_type": "TMP_InputField"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "NameInput",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0.5, 0.5], "anchorMax": [0.5, 0.5],
"sizeDelta": [400, 50], "anchoredPosition": [0, 0]
}
}},
# Text Area child (clips text to input bounds)
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "InputTextArea", "parent": "NameInput"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "InputTextArea", "component_type": "RectMask2D"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "InputTextArea",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [-16, -8]}
}},
# Placeholder
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "InputPlaceholder", "parent": "InputTextArea"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "InputPlaceholder", "component_type": "TextMeshProUGUI"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "InputPlaceholder",
"component_type": "TextMeshProUGUI",
"properties": {"text": "Enter name...", "fontStyle": 2, "color": [0.5, 0.5, 0.5, 0.5]}
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "InputPlaceholder",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}
}},
# Actual text display
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "InputText", "parent": "InputTextArea"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "InputText", "component_type": "TextMeshProUGUI"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "InputText",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}
}}
])
# Step 2: Wire TMP_InputField references (CRITICAL)
batch_execute(fail_fast=True, commands=[
{"tool": "manage_components", "params": {
"action": "set_property", "target": "NameInput",
"component_type": "TMP_InputField", "property": "textViewport",
"value": {"name": "InputTextArea"}
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "NameInput",
"component_type": "TMP_InputField", "property": "textComponent",
"value": {"name": "InputText"}
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "NameInput",
"component_type": "TMP_InputField", "property": "placeholder",
"value": {"name": "InputPlaceholder"}
}}
])
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "SoundToggle", "parent": "MenuPanel"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "SoundToggle", "component_type": "Toggle"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "SoundToggle",
"component_type": "RectTransform",
"properties": {
"anchorMin": [0.5, 0.5], "anchorMax": [0.5, 0.5],
"sizeDelta": [200, 30], "anchoredPosition": [0, 0]
}
}},
# Background box
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "ToggleBG", "parent": "SoundToggle"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "ToggleBG", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "ToggleBG",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0.5], "anchorMax": [0, 0.5], "sizeDelta": [26, 26], "anchoredPosition": [13, 0]}
}},
# Checkmark
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "ToggleCheckmark", "parent": "ToggleBG"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "ToggleCheckmark", "component_type": "Image"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "ToggleCheckmark",
"component_type": "RectTransform",
"properties": {"anchorMin": [0.1, 0.1], "anchorMax": [0.9, 0.9], "sizeDelta": [0, 0]}
}},
# Label
{"tool": "manage_gameobject", "params": {
"action": "create", "name": "ToggleLabel", "parent": "SoundToggle"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "ToggleLabel", "component_type": "TextMeshProUGUI"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "ToggleLabel",
"component_type": "TextMeshProUGUI",
"properties": {"text": "Sound Effects", "fontSize": 18, "alignment": 513}
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "ToggleLabel",
"component_type": "RectTransform",
"properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [-35, 0], "anchoredPosition": [17.5, 0]}
}}
])
# Wire Toggle references (CRITICAL)
batch_execute(fail_fast=True, commands=[
{"tool": "manage_components", "params": {
"action": "set_property", "target": "SoundToggle",
"component_type": "Toggle", "property": "graphic",
"value": {"name": "ToggleCheckmark"}
}}
])
Layout groups auto-arrange child elements, so you can skip manual RectTransform positioning for children.
batch_execute(fail_fast=True, commands=[
{"tool": "manage_components", "params": {
"action": "add", "target": "MenuPanel",
"component_type": "VerticalLayoutGroup"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MenuPanel",
"component_type": "VerticalLayoutGroup",
"properties": {
"spacing": 10,
"childAlignment": 4,
"childForceExpandWidth": True,
"childForceExpandHeight": False,
"padding": {"left": 20, "right": 20, "top": 20, "bottom": 20}
}
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "MenuPanel",
"component_type": "ContentSizeFitter"
}},
{"tool": "manage_components", "params": {
"action": "set_property", "target": "MenuPanel",
"component_type": "ContentSizeFitter",
"properties": { "verticalFit": 2 }
}}
])
childAlignment values: 0=UpperLeft, 1=UpperCenter, 2=UpperRight, 3=MiddleLeft, 4=MiddleCenter, 5=MiddleRight, 6=LowerLeft, 7=LowerCenter, 8=LowerRight. ContentSizeFitter fit modes: 0=Unconstrained, 1=MinSize, 2=PreferredSize.
Combines multiple templates into a full menu screen in two batch calls (default 25 command limit per batch, configurable in Unity MCP Tools window up to 100). Assumes project_info has been read and activeInputHandler is known.
# Batch 1: Canvas + EventSystem + Panel + Title
batch_execute(fail_fast=True, commands=[
# Canvas
{"tool": "manage_gameobject", "params": {"action": "create", "name": "MenuCanvas"}},
{"tool": "manage_components", "params": {"action": "add", "target": "MenuCanvas", "component_type": "Canvas"}},
{"tool": "manage_components", "params": {"action": "add", "target": "MenuCanvas", "component_type": "CanvasScaler"}},
{"tool": "manage_components", "params": {"action": "add", "target": "MenuCanvas", "component_type": "GraphicRaycaster"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "MenuCanvas", "component_type": "Canvas", "property": "renderMode", "value": 0}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "MenuCanvas", "component_type": "CanvasScaler", "properties": {"uiScaleMode": 1, "referenceResolution": [1920, 1080]}}},
# EventSystem — use StandaloneInputModule OR InputSystemUIInputModule based on project_info
{"tool": "manage_gameobject", "params": {"action": "create", "name": "EventSystem"}},
{"tool": "manage_components", "params": {"action": "add", "target": "EventSystem", "component_type": "UnityEngine.EventSystems.EventSystem"}},
{"tool": "manage_components", "params": {"action": "add", "target": "EventSystem", "component_type": "UnityEngine.EventSystems.StandaloneInputModule"}},
# Panel (centered, 60% width)
{"tool": "manage_gameobject", "params": {"action": "create", "name": "MenuPanel", "parent": "MenuCanvas"}},
{"tool": "manage_components", "params": {"action": "add", "target": "MenuPanel", "component_type": "Image"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "MenuPanel", "component_type": "Image", "property": "color", "value": [0.1, 0.1, 0.15, 0.9]}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "MenuPanel", "component_type": "RectTransform", "properties": {"anchorMin": [0.2, 0.15], "anchorMax": [0.8, 0.85], "sizeDelta": [0, 0]}}},
{"tool": "manage_components", "params": {"action": "add", "target": "MenuPanel", "component_type": "VerticalLayoutGroup"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "MenuPanel", "component_type": "VerticalLayoutGroup", "properties": {"spacing": 20, "childAlignment": 4, "childForceExpandWidth": True, "childForceExpandHeight": False}}},
# Title
{"tool": "manage_gameobject", "params": {"action": "create", "name": "Title", "parent": "MenuPanel"}},
{"tool": "manage_components", "params": {"action": "add", "target": "Title", "component_type": "TextMeshProUGUI"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "Title", "component_type": "TextMeshProUGUI", "properties": {"text": "My Game", "fontSize": 64, "alignment": 514, "color": [1, 1, 1, 1]}}}
])
# Batch 2: Buttons
batch_execute(fail_fast=True, commands=[
# Play Button
{"tool": "manage_gameobject", "params": {"action": "create", "name": "PlayButton", "parent": "MenuPanel"}},
{"tool": "manage_components", "params": {"action": "add", "target": "PlayButton", "component_type": "Image"}},
{"tool": "manage_components", "params": {"action": "add", "target": "PlayButton", "component_type": "Button"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "PlayButton", "component_type": "Image", "property": "color", "value": [0.2, 0.6, 1.0, 1.0]}},
{"tool": "manage_gameobject", "params": {"action": "create", "name": "PlayLabel", "parent": "PlayButton"}},
{"tool": "manage_components", "params": {"action": "add", "target": "PlayLabel", "component_type": "TextMeshProUGUI"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "PlayLabel", "component_type": "TextMeshProUGUI", "properties": {"text": "Play", "fontSize": 32, "alignment": 514}}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "PlayLabel", "component_type": "RectTransform", "properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}}},
# Settings Button
{"tool": "manage_gameobject", "params": {"action": "create", "name": "SettingsButton", "parent": "MenuPanel"}},
{"tool": "manage_components", "params": {"action": "add", "target": "SettingsButton", "component_type": "Image"}},
{"tool": "manage_components", "params": {"action": "add", "target": "SettingsButton", "component_type": "Button"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "SettingsButton", "component_type": "Image", "property": "color", "value": [0.3, 0.3, 0.35, 1.0]}},
{"tool": "manage_gameobject", "params": {"action": "create", "name": "SettingsLabel", "parent": "SettingsButton"}},
{"tool": "manage_components", "params": {"action": "add", "target": "SettingsLabel", "component_type": "TextMeshProUGUI"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "SettingsLabel", "component_type": "TextMeshProUGUI", "properties": {"text": "Settings", "fontSize": 32, "alignment": 514}}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "SettingsLabel", "component_type": "RectTransform", "properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}}},
# Quit Button
{"tool": "manage_gameobject", "params": {"action": "create", "name": "QuitButton", "parent": "MenuPanel"}},
{"tool": "manage_components", "params": {"action": "add", "target": "QuitButton", "component_type": "Image"}},
{"tool": "manage_components", "params": {"action": "add", "target": "QuitButton", "component_type": "Button"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "QuitButton", "component_type": "Image", "property": "color", "value": [0.8, 0.2, 0.2, 1.0]}},
{"tool": "manage_gameobject", "params": {"action": "create", "name": "QuitLabel", "parent": "QuitButton"}},
{"tool": "manage_components", "params": {"action": "add", "target": "QuitLabel", "component_type": "TextMeshProUGUI"}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "QuitLabel", "component_type": "TextMeshProUGUI", "properties": {"text": "Quit", "fontSize": 32, "alignment": 514}}},
{"tool": "manage_components", "params": {"action": "set_property", "target": "QuitLabel", "component_type": "RectTransform", "properties": {"anchorMin": [0, 0], "anchorMax": [1, 1], "sizeDelta": [0, 0]}}}
])
| UI Element | Required Components | Notes |
|---|---|---|
| Canvas | Canvas + CanvasScaler + GraphicRaycaster | Root for all UI. One per screen. |
| EventSystem | EventSystem + input module (see below) | One per scene. Required for interaction. |
| Panel | Image + RectTransform sizing | Container. Set color for background. |
| Text | TextMeshProUGUI (or Text if no TMP) + RectTransform | Check packages.textmeshpro. |
| Button | Image + Button + child(TextMeshProUGUI) + RectTransform | Image = visual, Button = click handler. |
| Slider | Slider + Image + children + wire fillRect/handleRect | Won't function without wiring. |
| Toggle | Toggle + children + wire graphic | Wire checkmark Image to graphic. |
| Input Field | Image + TMP_InputField + children + wire textViewport/textComponent/placeholder | Won't function without wiring. |
| Layout Group | VerticalLayoutGroup / HorizontalLayoutGroup / GridLayoutGroup | Auto-arranges children; skip manual RectTransform on children. |
Unity has two input systems that affect UI interaction, script input handling, and EventSystem configuration. Always check project_info.activeInputHandler before creating EventSystems or writing input code.
# Read mcpforunity://project/info
# activeInputHandler: "Old" | "New" | "Both"
# packages.inputsystem: true/false (whether com.unity.inputsystem is installed)
Used when activeInputHandler is "Old". Uses StandaloneInputModule which reads from Input.GetAxis() / Input.GetButton().
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {"action": "create", "name": "EventSystem"}},
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.EventSystems.EventSystem"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.EventSystems.StandaloneInputModule"
}}
])
Script pattern (old Input Manager):
// Input.GetAxis / Input.GetKey — works with old Input Manager
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
transform.Translate(new Vector3(h, 0, v) * speed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.Space))
Jump();
if (Input.GetMouseButtonDown(0))
Fire();
}
Used when activeInputHandler is "New" or "Both". Uses InputSystemUIInputModule from the com.unity.inputsystem package.
batch_execute(fail_fast=True, commands=[
{"tool": "manage_gameobject", "params": {"action": "create", "name": "EventSystem"}},
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.EventSystems.EventSystem"
}},
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.InputSystem.UI.InputSystemUIInputModule"
}}
])
Script pattern (new Input System with PlayerInput component):
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
public float speed = 5f;
private Vector2 moveInput;
// Called by PlayerInput component via SendMessages or UnityEvents
public void OnMove(InputValue value)
{
moveInput = value.Get<Vector2>();
}
public void OnJump(InputValue value)
{
if (value.isPressed)
Jump();
}
void Update()
{
Vector3 move = new Vector3(moveInput.x, 0, moveInput.y);
transform.Translate(move * speed * Time.deltaTime);
}
}
activeInputHandler is "Both"Both systems are active simultaneously. For UI, prefer InputSystemUIInputModule. For gameplay scripts, either approach works — Input.GetAxis() still functions alongside the new Input System.
# UI: use new Input System module
{"tool": "manage_components", "params": {
"action": "add", "target": "EventSystem",
"component_type": "UnityEngine.InputSystem.UI.InputSystemUIInputModule"
}}
# Gameplay scripts: Input.GetAxis() still works in "Both" mode
# But prefer the new Input System for consistency
Gotcha: Adding
StandaloneInputModulewhenactiveInputHandleris"New"will cause a runtime error. Always check first.
# 1. Check Cinemachine availability
manage_camera(action="ping")
# 2. Ensure Brain on main camera
manage_camera(action="ensure_brain")
# 3. Create third-person camera with preset
manage_camera(action="create_camera", properties={
"name": "FollowCam", "preset": "third_person",
"follow": "Player", "lookAt": "Player", "priority": 20
})
# 4. Fine-tune body
manage_camera(action="set_body", target="FollowCam", properties={
"cameraDistance": 5.0, "shoulderOffset": [0.5, 0.5, 0]
})
# 5. Add camera shake
manage_camera(action="set_noise", target="FollowCam", properties={
"amplitudeGain": 0.3, "frequencyGain": 0.8
})
# 6. Verify with screenshot
manage_camera(action="screenshot", camera="FollowCam", include_image=True, max_resolution=512)
# 1. Read current cameras
# Read mcpforunity://scene/cameras
# 2. Create gameplay camera (highest priority = active by default)
manage_camera(action="create_camera", properties={
"name": "GameplayCam", "preset": "follow",
"follow": "Player", "lookAt": "Player", "priority": 10
})
# 3. Create cinematic camera (lower priority, activated on demand)
manage_camera(action="create_camera", properties={
"name": "CinematicCam", "preset": "dolly",
"lookAt": "CutsceneTarget", "priority": 5
})
# 4. Set blend transition
manage_camera(action="set_blend", properties={"style": "EaseInOut", "duration": 2.0})
# 5. Force cinematic camera for a cutscene
manage_camera(action="force_camera", target="CinematicCam")
# 6. Release override to return to priority-based selection
manage_camera(action="release_override")
# Tier 1 actions work with plain Unity Camera
manage_camera(action="create_camera", properties={
"name": "MainCam", "fieldOfView": 50
})
# Set lens
manage_camera(action="set_lens", target="MainCam", properties={
"fieldOfView": 60, "nearClipPlane": 0.1, "farClipPlane": 1000
})
# Point camera at target (uses manage_gameobject look_at under the hood)
manage_camera(action="set_target", target="MainCam", properties={
"lookAt": "Player"
})
# Screenshot from this camera
manage_camera(action="screenshot", camera="MainCam", include_image=True, max_resolution=512)
# 1. Read all cameras via resource
# Read mcpforunity://scene/cameras
# → Shows brain status, all Cinemachine cameras (priority, pipeline, targets),
# all Unity cameras (FOV, depth, brain)
# 2. Get brain status for blending info
manage_camera(action="get_brain_status")
# 3. List cameras via tool (alternative to resource)
manage_camera(action="list_cameras")
# 4. Multi-view screenshot to see from different angles
manage_camera(action="screenshot_multiview", max_resolution=480)
Use capture_source="scene_view" to capture the editor's Scene View viewport — useful for seeing gizmos, wireframes, grid, debug overlays, and objects without cameras.
# 1. Capture the Scene View as-is
manage_camera(action="screenshot", capture_source="scene_view", include_image=True)
# 2. Frame on a specific object first, then capture
manage_camera(action="screenshot", capture_source="scene_view",
view_target="Player", include_image=True, max_resolution=512)
# 3. Frame on UI Canvas (RectTransform bounds are supported)
manage_camera(action="screenshot", capture_source="scene_view",
view_target="Canvas", include_image=True)
# Limitations: scene_view does not support batch, view_position, view_rotation, or camera selection.
# Use capture_source="game_view" (default) for those features.
When com.unity.probuilder is installed, prefer ProBuilder shapes over primitive GameObjects for any geometry that needs editing, multi-material faces, or non-trivial shapes. Check availability first with manage_probuilder(action="ping").
See ProBuilder Workflow Guide for full reference with complex object examples.
| Need | Use Primitives | Use ProBuilder |
|---|---|---|
| Simple placeholder cube | manage_gameobject(action="create", primitive_type="Cube") | - |
| Editable geometry | - | manage_probuilder(action="create_shape", ...) |
| Per-face materials | - | set_face_material |
| Custom shapes (L-rooms, arches) | - | create_poly_shape or create_shape |
| Mesh editing (extrude, bevel) | - | Face/edge/vertex operations |
| Batch environment building | Either | ProBuilder + batch_execute |
# 1. Check ProBuilder availability
manage_probuilder(action="ping")
# 2. Create shapes (use batch for multiple)
batch_execute(commands=[
{"tool": "manage_probuilder", "params": {
"action": "create_shape",
"properties": {"shape_type": "Cube", "name": "Floor", "width": 20, "height": 0.2, "depth": 20}
}},
{"tool": "manage_probuilder", "params": {
"action": "create_shape",
"properties": {"shape_type": "Cube", "name": "Wall1", "width": 20, "height": 3, "depth": 0.3,
"position": [0, 1.5, 10]}
}},
{"tool": "manage_probuilder", "params": {
"action": "create_shape",
"properties": {"shape_type": "Cylinder", "name": "Pillar1", "radius": 0.4, "height": 3,
"position": [5, 1.5, 5]}
}},
])
# 3. Edit geometry (always get_mesh_info first!)
info = manage_probuilder(action="get_mesh_info", target="Wall1",
properties={"include": "faces"})
# Find direction="front" face, subdivide it, delete center for a window
# 4. Apply materials per face
manage_probuilder(action="set_face_material", target="Floor",
properties={"faceIndices": [0], "materialPath": "Assets/Materials/Stone.mat"})
# 5. Smooth organic shapes
manage_probuilder(action="auto_smooth", target="Pillar1",
properties={"angleThreshold": 45})
# 6. Screenshot to verify
manage_camera(action="screenshot", include_image=True, max_resolution=512)
Face indices change after every edit. Always re-query:
# WRONG: Assume face indices are stable
manage_probuilder(action="subdivide", target="Obj", properties={"faceIndices": [2]})
manage_probuilder(action="delete_faces", target="Obj", properties={"faceIndices": [5]}) # Index may be wrong!
# RIGHT: Re-query after each edit
manage_probuilder(action="subdivide", target="Obj", properties={"faceIndices": [2]})
info = manage_probuilder(action="get_mesh_info", target="Obj", properties={"include": "faces"})
# Find the correct face by direction/center, then delete
manage_probuilder(action="delete_faces", target="Obj", properties={"faceIndices": [correct_index]})
set_pivot: Broken -- vertex positions don't persist through mesh rebuild. Use center_pivot or Transform positioning.convert_to_probuilder: Broken -- MeshImporter throws. Create shapes natively with create_shape/create_poly_shape.subdivide: Uses ConnectElements.Connect (not traditional quad subdivision). Connects face midpoints.Add post-processing effects to a URP/HDRP scene using Volumes.
# 1. Check pipeline status and available effects
manage_graphics(action="ping")
# 2. List available volume effects for the active pipeline
manage_graphics(action="volume_list_effects")
# 3. Create a global post-processing volume with common effects
manage_graphics(action="volume_create", name="GlobalPostProcess", is_global=True,
effects=[
{"type": "Bloom", "parameters": {"intensity": 1.0, "threshold": 0.9, "scatter": 0.7}},
{"type": "Vignette", "parameters": {"intensity": 0.35}},
{"type": "Tonemapping", "parameters": {"mode": 1}},
{"type": "ColorAdjustments", "parameters": {"postExposure": 0.2, "contrast": 10}}
])
# 4. Verify the volume was created
# Read mcpforunity://scene/volumes
# 5. Fine-tune an effect parameter
manage_graphics(action="volume_set_effect", target="GlobalPostProcess",
effect="Bloom", parameters={"intensity": 1.5})
# 6. Screenshot to verify visual result
manage_camera(action="screenshot", include_image=True, max_resolution=512)
Tips:
ping first to confirm URP/HDRP is active. Volumes do nothing on Built-in RP.volume_list_effects to discover available effect types for the active pipeline (URP and HDRP have different sets).volume_get_info to inspect current effect parameters before modifying.volume_create_profile and reference it via profile_path on multiple volumes.Add a custom full-screen shader pass using URP Renderer Features.
# 1. Check pipeline and confirm URP
manage_graphics(action="ping")
# 2. Create a material for the full-screen effect
manage_material(action="create",
material_path="Assets/Materials/GrayscaleEffect.mat",
shader="Shader Graphs/GrayscaleFullScreen")
# 3. List current renderer features
manage_graphics(action="feature_list")
# 4. Add a FullScreenPassRendererFeature with the material
manage_graphics(action="feature_add",
feature_type="FullScreenPassRendererFeature",
name="GrayscalePass",
material="Assets/Materials/GrayscaleEffect.mat")
# 5. Verify it was added
manage_graphics(action="feature_list")
# 6. Toggle it on/off to compare
manage_graphics(action="feature_toggle", index=0, active=False) # disable
manage_camera(action="screenshot", include_image=True, max_resolution=512)
manage_graphics(action="feature_toggle", index=0, active=True) # re-enable
manage_camera(action="screenshot", include_image=True, max_resolution=512)
# 7. Reorder features if needed (execution order matters)
manage_graphics(action="feature_reorder", order=[1, 0, 2])
Tips:
feature_* actions return an error on HDRP or Built-in RP.mcpforunity://pipeline/renderer-features to inspect features without modifying.feature_reorder to control pass ordering.Set up lightmaps, light probes, and reflection probes for baked GI.
# 1. Set lights to Baked or Mixed mode
manage_components(action="set_property", target="Directional Light",
component_type="Light", properties={"lightmapBakeType": 1}) # 1 = Mixed
# 2. Mark static objects for lightmapping
manage_gameobject(action="modify", target="Environment",
component_properties={"StaticFlags": "ContributeGI"})
# 3. Configure lightmap settings
manage_graphics(action="bake_get_settings")
manage_graphics(action="bake_set_settings", settings={
"lightmapper": 1, # 1 = Progressive GPU
"directSamples": 32,
"indirectSamples": 128,
"maxBounces": 4,
"lightmapResolution": 40
})
# 4. Place light probes for dynamic objects
manage_graphics(action="bake_create_light_probe_group", name="MainProbeGrid",
position=[0, 1.5, 0], grid_size=[5, 3, 5], spacing=3.0)
# 5. Place a reflection probe for an interior room
manage_graphics(action="bake_create_reflection_probe", name="RoomReflection",
position=[0, 2, 0], size=[8, 4, 8], resolution=256,
hdr=True, box_projection=True)
# 6. Start async bake
manage_graphics(action="bake_start", async_bake=True)
# 7. Poll bake status
manage_graphics(action="bake_status")
# Repeat until complete
# 8. Bake the reflection probe separately if needed
manage_graphics(action="bake_reflection_probe", target="RoomReflection")
# 9. Check rendering stats after bake
manage_graphics(action="stats_get")
Tips:
bake_start will fail.bake_cancel to abort a long bake.bake_clear removes all baked data (lightmaps, probes). Use before re-baking from scratch.async_bake=True (default) and poll bake_status periodically.# 1. Check what's installed
manage_packages(action="ping")
manage_packages(action="list_packages")
# Poll status until complete
manage_packages(action="status", job_id="<job_id>")
# 2. Install the package
manage_packages(action="add_package", package="com.unity.inputsystem")
# Poll until domain reload completes
manage_packages(action="status", job_id="<job_id>")
# 3. Verify no compilation errors
read_console(types=["error"], count=10)
# 4. Confirm it's installed
manage_packages(action="get_package_info", package="com.unity.inputsystem")
# 1. Add the OpenUPM scoped registry
manage_packages(
action="add_registry",
name="OpenUPM",
url="https://package.openupm.com",
scopes=["com.cysharp"]
)
# 2. Force resolution to pick up the new registry
manage_packages(action="resolve_packages")
# 3. Install a package from OpenUPM
manage_packages(action="add_package", package="com.cysharp.unitask")
manage_packages(action="status", job_id="<job_id>")
# 1. Check dependencies before removing
manage_packages(action="remove_package", package="com.unity.modules.ui")
# If blocked: "Cannot remove: 3 package(s) depend on it"
# 2. Force removal if you're sure
manage_packages(action="remove_package", package="com.unity.modules.ui", force=True)
manage_packages(action="status", job_id="<job_id>")
# Git URLs trigger a security warning — ensure the source is trusted
manage_packages(
action="add_package",
package="https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity"
)
manage_packages(action="status", job_id="<job_id>")
Use deploy_package to copy your local MCPForUnity source into the project's installed package location. This bypasses the UI dialog and triggers recompilation automatically.
# Prerequisites: Set the MCPForUnity source path in Advanced Settings first.
# 1. Make code changes (e.g., edit C# tools)
# script_apply_edits or create_script as needed
# 2. Deploy the updated package (copies source → installed package, creates backup)
manage_editor(action="deploy_package")
# 3. Wait for recompilation to finish
refresh_unity(mode="force", compile="request", wait_for_ready=True)
# 4. Check for compilation errors
read_console(types=["error"], count=10, include_stacktrace=True)
# 5. Test the changes
run_tests(mode="EditMode")
# Restore from the automatic pre-deployment backup
manage_editor(action="restore_package")
# Wait for recompilation
refresh_unity(mode="force", compile="request", wait_for_ready=True)
These tools live in the opt-in
docsgroup. Activate it first:manage_tools(action="activate", group="docs")
Use unity_reflect and unity_docs to verify Unity APIs before writing C# code. This prevents hallucinated or outdated API references.
Trust hierarchy: reflection (live runtime) > project assets > official docs.
# Step 1: Search for the type you need
unity_reflect(action="search", query="NavMesh")
# → Returns matching types: NavMeshAgent, NavMeshPath, NavMeshHit, etc.
# Step 2: Get member summary for the type
unity_reflect(action="get_type", class_name="UnityEngine.AI.NavMeshAgent")
# → Returns all methods, properties, fields (names only)
# Step 3: Get full signature for specific members you plan to use
unity_reflect(action="get_member", class_name="NavMeshAgent", member_name="SetDestination")
# → Returns parameter types, return type, all overloads
# Step 4: Get official docs for usage patterns and examples
unity_docs(action="get_doc", class_name="NavMeshAgent", member_name="SetDestination")
# → Returns description, signatures, parameters, code examples
Use unity_docs lookup action to search multiple APIs in a single call:
# Search ScriptReference + Manual in parallel (+ package docs if package/pkg_version provided)
unity_docs(action="lookup", queries="Physics.Raycast,NavMeshAgent,Light2D")
# Include package docs in the search
unity_docs(action="lookup", query="VolumeProfile",
package="com.unity.render-pipelines.universal", pkg_version="17.0")
The lookup action automatically searches project assets for asset-related queries:
# This searches both docs AND project assets for shader-related content
unity_docs(action="lookup", query="Lit shader")
# → Returns doc hits + matching project assets (shaders, materials, etc.)
# Fetch Unity Manual pages (execution order, scripting concepts, etc.)
unity_docs(action="get_manual", slug="execution-order")
# Fetch package-specific documentation
unity_docs(action="get_package_doc",
package="com.unity.render-pipelines.universal",
page="2d-index", pkg_version="17.0")
# Specify Unity version for version-specific docs
unity_docs(action="get_doc", class_name="Camera", member_name="main", version="6000.0.38f1")
# Use reflection to check what's actually available in the running editor
unity_reflect(action="search", query="InputAction", scope="packages")
Use batch_execute to search for multiple things in a single call instead of calling find_gameobjects repeatedly:
# Instead of 4 separate find_gameobjects calls, batch them:
batch_execute(commands=[
{"tool": "find_gameobjects", "params": {"search_term": "Camera", "search_method": "by_component"}},
{"tool": "find_gameobjects", "params": {"search_term": "Rigidbody", "search_method": "by_component"}},
{"tool": "find_gameobjects", "params": {"search_term": "Player", "search_method": "by_tag"}},
{"tool": "find_gameobjects", "params": {"search_term": "GameManager", "search_method": "by_name"}}
])
# Returns array of results, one per command
# Find all enemies
enemies = find_gameobjects(search_term="Enemy", search_method="by_tag")
# Update health on all enemies
commands = []
for enemy_id in enemies["ids"]:
commands.append({
"tool": "manage_components",
"params": {
"action": "set_property",
"target": enemy_id,
"component_type": "EnemyHealth",
"property": "maxHealth",
"value": 100
}
})
# Execute in batches
for i in range(0, len(commands), 25):
batch_execute(commands=commands[i:i+25], parallel=True)
import random
commands = []
for i in range(20):
commands.append({
"tool": "manage_gameobject",
"params": {
"action": "create",
"name": f"Tree_{i}",
"primitive_type": "Capsule",
"position": [random.uniform(-50, 50), 0, random.uniform(-50, 50)],
"scale": [1, random.uniform(2, 5), 1]
}
})
batch_execute(commands=commands, parallel=True)
# Find all temporary objects
temps = find_gameobjects(search_term="Temp_", search_method="by_name")
# Delete in batch
commands = [
{"tool": "manage_gameobject", "params": {"action": "delete", "target": id}}
for id in temps["ids"]
]
batch_execute(commands=commands, fail_fast=False)
try:
apply_text_edits(uri=script_uri, edits=[...], precondition_sha256=old_sha)
except Exception as e:
if "stale_file" in str(e):
# Re-fetch SHA
new_sha = get_sha(uri=script_uri)
# Retry with new SHA
apply_text_edits(uri=script_uri, edits=[...], precondition_sha256=new_sha["sha256"])
# After domain reload, connection may be lost
# Wait and retry pattern:
import time
max_retries = 5
for attempt in range(max_retries):
try:
editor_state = read_resource("mcpforunity://editor/state")
if editor_state["ready_for_tools"]:
break
except:
time.sleep(2 ** attempt) # Exponential backoff
# If tools fail due to compilation:
# 1. Check console for errors
errors = read_console(types=["error"], count=20)
# 2. Fix the script errors
# ... edit scripts ...
# 3. Force refresh
refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True)
# 4. Verify clean console
errors = read_console(types=["error"], count=5)
if not errors["messages"]:
# Safe to proceed with tools
pass