Unity-MCP skills (#659)

* Initial Upload for Skills

* Update unity-mcp-skill/SKILL.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Refine language in Unity-MCP Operator Guide

Updated wording for clarity and consistency in the guide.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
beta
Shutong Wu 2026-02-03 10:14:10 -05:00 committed by GitHub
parent 74236b6f66
commit 20e822b60f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 1922 additions and 0 deletions

214
unity-mcp-skill/SKILL.md Normal file
View File

@ -0,0 +1,214 @@
---
name: unity-mcp-orchestrator
description: Orchestrate Unity Editor via MCP (Model Context Protocol) tools and resources. Use when working with Unity projects through MCP for Unity - creating/modifying GameObjects, editing scripts, managing scenes, running tests, or any Unity Editor automation. Provides best practices, tool schemas, and workflow patterns for effective Unity-MCP integration.
---
# Unity-MCP Operator Guide
This skill helps you effectively use the Unity Editor with MCP tools and resources.
## Quick Start: Resource-First Workflow
**Always read relevant resources before using tools.** This prevents errors and provides the necessary context.
```
1. Check editor state → mcpforunity://editor/state
2. Understand the scene → mcpforunity://scene/gameobject-api
3. Find what you need → find_gameobjects or resources
4. Take action → tools (manage_gameobject, create_script, script_apply_edits, apply_text_edits, validate_script, delete_script, get_sha, etc.)
5. Verify results → read_console, capture_screenshot (in manage_scene), resources
```
## Critical Best Practices
### 1. After Writing/Editing Scripts: Always Refresh and Check Console
```python
# After create_script or script_apply_edits:
refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True)
read_console(types=["error"], count=10, include_stacktrace=True)
```
**Why:** Unity must compile scripts before they're usable. Compilation errors block all tool execution.
### 2. Use `batch_execute` for Multiple Operations
```python
# 10-100x faster than sequential calls
batch_execute(
commands=[
{"tool": "manage_gameobject", "params": {"action": "create", "name": "Cube1", "primitive_type": "Cube"}},
{"tool": "manage_gameobject", "params": {"action": "create", "name": "Cube2", "primitive_type": "Cube"}},
{"tool": "manage_gameobject", "params": {"action": "create", "name": "Cube3", "primitive_type": "Cube"}}
],
parallel=True # Read-only operations can run in parallel
)
```
**Max 25 commands per batch.** Use `fail_fast=True` for dependent operations.
### 3. Use `screenshot` in manage_scene to Verify Visual Results
```python
# Via manage_scene
manage_scene(action="screenshot") # Returns base64 image
# After creating/modifying objects, verify visually:
# 1. Create objects
# 2. capture screenshot
# 3. Analyze if result matches intent
```
### 4. Check Console After Major Changes
```python
read_console(
action="get",
types=["error", "warning"], # Focus on problems
count=10,
format="detailed"
)
```
### 5. Always Check `editor_state` Before Complex Operations
```python
# Read mcpforunity://editor/state to check:
# - is_compiling: Wait if true
# - is_domain_reload_pending: Wait if true
# - ready_for_tools: Only proceed if true
# - blocking_reasons: Why tools might fail
```
## Parameter Type Conventions
### Vectors (position, rotation, scale, color)
```python
# Both forms accepted:
position=[1.0, 2.0, 3.0] # List
position="[1.0, 2.0, 3.0]" # JSON string
```
### Booleans
```python
# Both forms accepted:
include_inactive=True # Boolean
include_inactive="true" # String
```
### Colors
```python
# Auto-detected format:
color=[255, 0, 0, 255] # 0-255 range
color=[1.0, 0.0, 0.0, 1.0] # 0.0-1.0 normalized (auto-converted)
```
### Paths
```python
# Assets-relative (default):
path="Assets/Scripts/MyScript.cs"
# URI forms:
uri="mcpforunity://path/Assets/Scripts/MyScript.cs"
uri="file:///full/path/to/file.cs"
```
## Core Tool Categories
| Category | Key Tools | Use For |
|----------|-----------|---------|
| **Scene** | `manage_scene`, `find_gameobjects` | Scene operations, finding objects |
| **Objects** | `manage_gameobject`, `manage_components` | Creating/modifying GameObjects |
| **Scripts** | `create_script`, `script_apply_edits`, `refresh_unity` | C# code management |
| **Assets** | `manage_asset`, `manage_prefabs` | Asset operations |
| **Editor** | `manage_editor`, `execute_menu_item`, `read_console` | Editor control |
| **Testing** | `run_tests`, `get_test_job` | Unity Test Framework |
| **Batch** | `batch_execute` | Parallel/bulk operations |
## Common Workflows
### Creating a New Script and Using It
```python
# 1. Create the script
create_script(
path="Assets/Scripts/PlayerController.cs",
contents="using UnityEngine;\n\npublic class PlayerController : MonoBehaviour\n{\n void Update() { }\n}"
)
# 2. CRITICAL: Refresh and wait for compilation
refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True)
# 3. Check for compilation errors
read_console(types=["error"], count=10)
# 4. Only then attach to GameObject
manage_gameobject(action="modify", target="Player", components_to_add=["PlayerController"])
```
### Finding and Modifying GameObjects
```python
# 1. Find by name/tag/component (returns IDs only)
result = find_gameobjects(search_term="Enemy", search_method="by_tag", page_size=50)
# 2. Get full data via resource
# mcpforunity://scene/gameobject/{instance_id}
# 3. Modify using the ID
manage_gameobject(action="modify", target=instance_id, position=[10, 0, 0])
```
### Running and Monitoring Tests
```python
# 1. Start test run (async)
result = run_tests(mode="EditMode", test_names=["MyTests.TestSomething"])
job_id = result["job_id"]
# 2. Poll for completion
result = get_test_job(job_id=job_id, wait_timeout=60, include_failed_tests=True)
```
## Pagination Pattern
Large queries return paginated results. Always follow `next_cursor`:
```python
cursor = 0
all_items = []
while True:
result = manage_scene(action="get_hierarchy", page_size=50, cursor=cursor)
all_items.extend(result["data"]["items"])
if not result["data"].get("next_cursor"):
break
cursor = result["data"]["next_cursor"]
```
## Multi-Instance Workflow
When multiple Unity Editors are running:
```python
# 1. List instances via resource: mcpforunity://instances
# 2. Set active instance
set_active_instance(instance="MyProject@abc123")
# 3. All subsequent calls route to that instance
```
## Error Recovery
| Symptom | Cause | Solution |
|---------|-------|----------|
| Tools return "busy" | Compilation in progress | Wait, check `editor_state` |
| "stale_file" error | File changed since SHA | Re-fetch SHA with `get_sha`, retry |
| Connection lost | Domain reload | Wait ~5s, reconnect |
| Commands fail silently | Wrong instance | Check `set_active_instance` |
## Reference Files
For detailed schemas and examples:
- **[tools-reference.md](references/tools-reference.md)**: Complete tool documentation with all parameters
- **[resources-reference.md](references/resources-reference.md)**: All available resources and their data
- **[workflows.md](references/workflows.md)**: Extended workflow examples and patterns

View File

@ -0,0 +1,493 @@
# Unity-MCP Resources Reference
Resources provide read-only access to Unity state. Use resources to inspect before using tools to modify.
## Table of Contents
- [Editor State Resources](#editor-state-resources)
- [Scene & GameObject Resources](#scene--gameobject-resources)
- [Prefab Resources](#prefab-resources)
- [Project Resources](#project-resources)
- [Instance Resources](#instance-resources)
- [Test Resources](#test-resources)
---
## URI Scheme
All resources use `mcpforunity://` scheme:
```
mcpforunity://{category}/{resource_path}[?query_params]
```
**Categories:** `editor`, `scene`, `prefab`, `project`, `menu-items`, `custom-tools`, `tests`, `instances`
---
## Editor State Resources
### mcpforunity://editor/state
**Purpose:** Editor readiness snapshot - check before tool operations.
**Returns:**
```json
{
"unity_version": "2022.3.10f1",
"is_compiling": false,
"is_domain_reload_pending": false,
"play_mode": {
"is_playing": false,
"is_paused": false
},
"active_scene": {
"path": "Assets/Scenes/Main.unity",
"name": "Main"
},
"ready_for_tools": true,
"blocking_reasons": [],
"recommended_retry_after_ms": null,
"staleness": {
"age_ms": 150,
"is_stale": false
}
}
```
**Key Fields:**
- `ready_for_tools`: Only proceed if `true`
- `is_compiling`: Wait if `true`
- `blocking_reasons`: Array explaining why tools might fail
- `recommended_retry_after_ms`: Suggested wait time
### mcpforunity://editor/selection
**Purpose:** Currently selected objects.
**Returns:**
```json
{
"activeObject": "Player",
"activeGameObject": "Player",
"activeInstanceID": 12345,
"count": 3,
"gameObjects": ["Player", "Enemy", "Wall"],
"assetGUIDs": []
}
```
### mcpforunity://editor/active-tool
**Purpose:** Current editor tool state.
**Returns:**
```json
{
"activeTool": "Move",
"isCustom": false,
"pivotMode": "Center",
"pivotRotation": "Global"
}
```
### mcpforunity://editor/windows
**Purpose:** All open editor windows.
**Returns:**
```json
{
"windows": [
{
"title": "Scene",
"typeName": "UnityEditor.SceneView",
"isFocused": true,
"position": {"x": 0, "y": 0, "width": 800, "height": 600}
}
]
}
```
### mcpforunity://editor/prefab-stage
**Purpose:** Current prefab editing context.
**Returns:**
```json
{
"isOpen": true,
"assetPath": "Assets/Prefabs/Player.prefab",
"prefabRootName": "Player",
"isDirty": false
}
```
---
## Scene & GameObject Resources
### mcpforunity://scene/gameobject-api
**Purpose:** Documentation for GameObject resources (read this first).
### mcpforunity://scene/gameobject/{instance_id}
**Purpose:** Basic GameObject data (metadata, no component properties).
**Parameters:**
- `instance_id` (int): GameObject instance ID from `find_gameobjects`
**Returns:**
```json
{
"instanceID": 12345,
"name": "Player",
"tag": "Player",
"layer": 8,
"layerName": "Player",
"active": true,
"activeInHierarchy": true,
"isStatic": false,
"transform": {
"position": [0, 1, 0],
"rotation": [0, 0, 0],
"scale": [1, 1, 1]
},
"parent": {"instanceID": 0},
"children": [{"instanceID": 67890}],
"componentTypes": ["Transform", "Rigidbody", "PlayerController"],
"path": "/Player"
}
```
### mcpforunity://scene/gameobject/{instance_id}/components
**Purpose:** All components with full property serialization (paginated).
**Parameters:**
- `instance_id` (int): GameObject instance ID
- `page_size` (int): Default 25, max 100
- `cursor` (int): Pagination cursor
- `include_properties` (bool): Default true, set false for just types
**Returns:**
```json
{
"gameObjectID": 12345,
"gameObjectName": "Player",
"components": [
{
"type": "Transform",
"properties": {
"position": {"x": 0, "y": 1, "z": 0},
"rotation": {"x": 0, "y": 0, "z": 0, "w": 1}
}
},
{
"type": "Rigidbody",
"properties": {
"mass": 1.0,
"useGravity": true
}
}
],
"cursor": 0,
"pageSize": 25,
"nextCursor": null,
"hasMore": false
}
```
### mcpforunity://scene/gameobject/{instance_id}/component/{component_name}
**Purpose:** Single component with full properties.
**Parameters:**
- `instance_id` (int): GameObject instance ID
- `component_name` (string): e.g., "Rigidbody", "Camera", "Transform"
**Returns:**
```json
{
"gameObjectID": 12345,
"gameObjectName": "Player",
"component": {
"type": "Rigidbody",
"properties": {
"mass": 1.0,
"drag": 0,
"angularDrag": 0.05,
"useGravity": true,
"isKinematic": false
}
}
}
```
---
## Prefab Resources
### mcpforunity://prefab-api
**Purpose:** Documentation for prefab resources.
### mcpforunity://prefab/{encoded_path}
**Purpose:** Prefab asset information.
**Parameters:**
- `encoded_path` (string): URL-encoded path, e.g., `Assets%2FPrefabs%2FPlayer.prefab`
**Path Encoding:**
```
Assets/Prefabs/Player.prefab → Assets%2FPrefabs%2FPlayer.prefab
```
**Returns:**
```json
{
"assetPath": "Assets/Prefabs/Player.prefab",
"guid": "abc123...",
"prefabType": "Regular",
"rootObjectName": "Player",
"rootComponentTypes": ["Transform", "PlayerController"],
"childCount": 5,
"isVariant": false,
"parentPrefab": null
}
```
### mcpforunity://prefab/{encoded_path}/hierarchy
**Purpose:** Full prefab hierarchy with nested prefab info.
**Returns:**
```json
{
"prefabPath": "Assets/Prefabs/Player.prefab",
"total": 6,
"items": [
{
"name": "Player",
"instanceId": 12345,
"path": "/Player",
"activeSelf": true,
"childCount": 2,
"componentTypes": ["Transform", "PlayerController"]
},
{
"name": "Model",
"path": "/Player/Model",
"isNestedPrefab": true,
"nestedPrefabPath": "Assets/Prefabs/PlayerModel.prefab"
}
]
}
```
---
## Project Resources
### mcpforunity://project/info
**Purpose:** Static project configuration.
**Returns:**
```json
{
"projectRoot": "/Users/dev/MyProject",
"projectName": "MyProject",
"unityVersion": "2022.3.10f1",
"platform": "StandaloneWindows64",
"assetsPath": "/Users/dev/MyProject/Assets"
}
```
### mcpforunity://project/tags
**Purpose:** All tags defined in TagManager.
**Returns:**
```json
["Untagged", "Respawn", "Finish", "EditorOnly", "MainCamera", "Player", "GameController", "Enemy"]
```
### mcpforunity://project/layers
**Purpose:** All layers with indices (0-31).
**Returns:**
```json
{
"0": "Default",
"1": "TransparentFX",
"2": "Ignore Raycast",
"4": "Water",
"5": "UI",
"8": "Player",
"9": "Enemy"
}
```
### mcpforunity://menu-items
**Purpose:** All available Unity menu items.
**Returns:**
```json
[
"File/New Scene",
"File/Open Scene",
"File/Save",
"Edit/Undo",
"Edit/Redo",
"GameObject/Create Empty",
"GameObject/3D Object/Cube",
"Window/General/Console"
]
```
### mcpforunity://custom-tools
**Purpose:** Custom tools available in the active Unity project.
**Returns:**
```json
{
"project_id": "MyProject",
"tool_count": 3,
"tools": [
{
"name": "capture_screenshot",
"description": "Capture screenshots in Unity",
"parameters": [
{"name": "filename", "type": "string", "required": true},
{"name": "width", "type": "int", "required": false},
{"name": "height", "type": "int", "required": false}
]
}
]
}
```
---
## Instance Resources
### mcpforunity://instances
**Purpose:** All running Unity Editor instances (for multi-instance workflows).
**Returns:**
```json
{
"transport": "http",
"instance_count": 2,
"instances": [
{
"id": "MyProject@abc123",
"name": "MyProject",
"hash": "abc123",
"unity_version": "2022.3.10f1",
"connected_at": "2024-01-15T10:30:00Z"
},
{
"id": "TestProject@def456",
"name": "TestProject",
"hash": "def456",
"unity_version": "2022.3.10f1",
"connected_at": "2024-01-15T11:00:00Z"
}
],
"warnings": []
}
```
**Use with:** `set_active_instance(instance="MyProject@abc123")`
---
## Test Resources
### mcpforunity://tests
**Purpose:** All tests in the project.
**Returns:**
```json
[
{
"name": "TestSomething",
"full_name": "MyTests.TestSomething",
"mode": "EditMode"
},
{
"name": "TestOther",
"full_name": "MyTests.TestOther",
"mode": "PlayMode"
}
]
```
### mcpforunity://tests/{mode}
**Purpose:** Tests filtered by mode.
**Parameters:**
- `mode` (string): "EditMode" or "PlayMode"
**Example:** `mcpforunity://tests/EditMode`
---
## Best Practices
### 1. Check Editor State First
```python
# Before any complex operation:
# Read mcpforunity://editor/state
# Check ready_for_tools == true
```
### 2. Use Find Then Read Pattern
```python
# 1. find_gameobjects to get IDs
result = find_gameobjects(search_term="Player")
# 2. Read resource for full data
# mcpforunity://scene/gameobject/{id}
```
### 3. Paginate Large Queries
```python
# Start with include_properties=false for component lists
# mcpforunity://scene/gameobject/{id}/components?include_properties=false&page_size=25
# Then read specific components as needed
# mcpforunity://scene/gameobject/{id}/component/Rigidbody
```
### 4. URL-Encode Prefab Paths
```python
# Wrong:
# mcpforunity://prefab/Assets/Prefabs/Player.prefab
# Correct:
# mcpforunity://prefab/Assets%2FPrefabs%2FPlayer.prefab
```
### 5. Multi-Instance Awareness
```python
# Always check mcpforunity://instances when:
# - First connecting
# - Commands fail unexpectedly
# - Working with multiple projects
```

View File

@ -0,0 +1,606 @@
# Unity-MCP Tools Reference
Complete reference for all MCP tools. Each tool includes parameters, types, and usage examples.
## Table of Contents
- [Infrastructure Tools](#infrastructure-tools)
- [Scene Tools](#scene-tools)
- [GameObject Tools](#gameobject-tools)
- [Script Tools](#script-tools)
- [Asset Tools](#asset-tools)
- [Material & Shader Tools](#material--shader-tools)
- [Editor Control Tools](#editor-control-tools)
- [Testing Tools](#testing-tools)
---
## Infrastructure Tools
### batch_execute
Execute multiple MCP commands in a single batch (10-100x faster).
```python
batch_execute(
commands=[ # list[dict], required, max 25
{"tool": "tool_name", "params": {...}},
...
],
parallel=False, # bool, optional - run read-only ops in parallel
fail_fast=False, # bool, optional - stop on first failure
max_parallelism=None # int, optional - max parallel workers
)
```
### set_active_instance
Route commands to a specific Unity instance (multi-instance workflows).
```python
set_active_instance(
instance="ProjectName@abc123" # str, required - Name@hash or hash prefix
)
```
### refresh_unity
Refresh asset database and trigger script compilation.
```python
refresh_unity(
mode="if_dirty", # "if_dirty" | "force"
scope="all", # "assets" | "scripts" | "all"
compile="none", # "none" | "request"
wait_for_ready=True # bool - wait until editor ready
)
```
---
## Scene Tools
### manage_scene
Scene CRUD operations and hierarchy queries.
```python
# Get hierarchy (paginated)
manage_scene(
action="get_hierarchy",
page_size=50, # int, default 50, max 500
cursor=0, # int, pagination cursor
parent=None, # str|int, optional - filter by parent
include_transform=False # bool - include local transforms
)
# Screenshot
manage_scene(action="screenshot") # Returns base64 PNG
# Other actions
manage_scene(action="get_active") # Current scene info
manage_scene(action="get_build_settings") # Build settings
manage_scene(action="create", name="NewScene", path="Assets/Scenes/")
manage_scene(action="load", path="Assets/Scenes/Main.unity")
manage_scene(action="save")
```
### find_gameobjects
Search for GameObjects (returns instance IDs only).
```python
find_gameobjects(
search_term="Player", # str, required
search_method="by_name", # "by_name"|"by_tag"|"by_layer"|"by_component"|"by_path"|"by_id"
include_inactive=False, # bool|str
page_size=50, # int, default 50, max 500
cursor=0 # int, pagination cursor
)
# Returns: {"ids": [12345, 67890], "next_cursor": 50, ...}
```
---
## GameObject Tools
### manage_gameobject
Create, modify, delete, duplicate GameObjects.
```python
# Create
manage_gameobject(
action="create",
name="MyCube", # str, required
primitive_type="Cube", # "Cube"|"Sphere"|"Capsule"|"Cylinder"|"Plane"|"Quad"
position=[0, 1, 0], # list[float] or JSON string "[0,1,0]"
rotation=[0, 45, 0], # euler angles
scale=[1, 1, 1],
components_to_add=["Rigidbody", "BoxCollider"],
save_as_prefab=False,
prefab_path="Assets/Prefabs/MyCube.prefab"
)
# Modify
manage_gameobject(
action="modify",
target="Player", # name, path, or instance ID
search_method="by_name", # how to find target
position=[10, 0, 0],
rotation=[0, 90, 0],
scale=[2, 2, 2],
set_active=True,
layer="Player",
components_to_add=["AudioSource"],
components_to_remove=["OldComponent"],
component_properties={ # nested dict for property setting
"Rigidbody": {
"mass": 10.0,
"useGravity": True
}
}
)
# Delete
manage_gameobject(action="delete", target="OldObject")
# Duplicate
manage_gameobject(
action="duplicate",
target="Player",
new_name="Player2",
offset=[5, 0, 0] # position offset from original
)
# Move relative
manage_gameobject(
action="move_relative",
target="Player",
reference_object="Enemy", # optional reference
direction="left", # "left"|"right"|"up"|"down"|"forward"|"back"
distance=5.0,
world_space=True
)
```
### manage_components
Add, remove, or set properties on components.
```python
# Add component
manage_components(
action="add",
target=12345, # instance ID (preferred) or name
component_type="Rigidbody",
search_method="by_id"
)
# Remove component
manage_components(
action="remove",
target="Player",
component_type="OldScript"
)
# Set single property
manage_components(
action="set_property",
target=12345,
component_type="Rigidbody",
property="mass",
value=5.0
)
# Set multiple properties
manage_components(
action="set_property",
target=12345,
component_type="Transform",
properties={
"position": [1, 2, 3],
"localScale": [2, 2, 2]
}
)
```
---
## Script Tools
### create_script
Create a new C# script.
```python
create_script(
path="Assets/Scripts/MyScript.cs", # str, required
contents='''using UnityEngine;
public class MyScript : MonoBehaviour
{
void Start() { }
void Update() { }
}''',
script_type="MonoBehaviour", # optional hint
namespace="MyGame" # optional namespace
)
```
### script_apply_edits
Apply structured edits to C# scripts (safer than raw text edits).
```python
script_apply_edits(
name="MyScript", # script name (no .cs)
path="Assets/Scripts", # folder path
edits=[
# Replace entire method
{
"op": "replace_method",
"methodName": "Update",
"replacement": "void Update() { transform.Rotate(Vector3.up); }"
},
# Insert new method
{
"op": "insert_method",
"afterMethod": "Start",
"code": "void OnEnable() { Debug.Log(\"Enabled\"); }"
},
# Delete method
{
"op": "delete_method",
"methodName": "OldMethod"
},
# Anchor-based insert
{
"op": "anchor_insert",
"anchor": "void Start()",
"position": "before", # "before" | "after"
"text": "// Called before Start\n"
},
# Regex replace
{
"op": "regex_replace",
"pattern": "Debug\\.Log\\(",
"text": "Debug.LogWarning("
},
# Prepend/append to file
{"op": "prepend", "text": "// File header\n"},
{"op": "append", "text": "\n// File footer"}
]
)
```
### apply_text_edits
Apply precise character-position edits (1-indexed lines/columns).
```python
apply_text_edits(
uri="mcpforunity://path/Assets/Scripts/MyScript.cs",
edits=[
{
"startLine": 10,
"startCol": 5,
"endLine": 10,
"endCol": 20,
"newText": "replacement text"
}
],
precondition_sha256="abc123...", # optional, prevents stale edits
strict=True # optional, stricter validation
)
```
### validate_script
Check script for syntax/semantic errors.
```python
validate_script(
uri="mcpforunity://path/Assets/Scripts/MyScript.cs",
level="standard", # "basic" | "standard"
include_diagnostics=True # include full error details
)
```
### get_sha
Get file hash without content (for preconditions).
```python
get_sha(uri="mcpforunity://path/Assets/Scripts/MyScript.cs")
# Returns: {"sha256": "...", "lengthBytes": 1234, "lastModifiedUtc": "..."}
```
### delete_script
Delete a script file.
```python
delete_script(uri="mcpforunity://path/Assets/Scripts/OldScript.cs")
```
---
## Asset Tools
### manage_asset
Asset operations: search, import, create, modify, delete.
```python
# Search assets (paginated)
manage_asset(
action="search",
path="Assets", # search scope
search_pattern="*.prefab", # glob or "t:MonoScript" filter
filter_type="Prefab", # optional type filter
page_size=25, # keep small to avoid large payloads
page_number=1, # 1-based
generate_preview=False # avoid base64 bloat
)
# Get asset info
manage_asset(action="get_info", path="Assets/Prefabs/Player.prefab")
# Create asset
manage_asset(
action="create",
path="Assets/Materials/NewMaterial.mat",
asset_type="Material",
properties={"color": [1, 0, 0, 1]}
)
# Duplicate/move/rename
manage_asset(action="duplicate", path="Assets/A.prefab", destination="Assets/B.prefab")
manage_asset(action="move", path="Assets/A.prefab", destination="Assets/Prefabs/A.prefab")
manage_asset(action="rename", path="Assets/A.prefab", destination="Assets/B.prefab")
# Create folder
manage_asset(action="create_folder", path="Assets/NewFolder")
# Delete
manage_asset(action="delete", path="Assets/OldAsset.asset")
```
### manage_prefabs
Headless prefab operations.
```python
# Get prefab info
manage_prefabs(action="get_info", prefab_path="Assets/Prefabs/Player.prefab")
# Get prefab hierarchy
manage_prefabs(action="get_hierarchy", prefab_path="Assets/Prefabs/Player.prefab")
# Create prefab from scene GameObject
manage_prefabs(
action="create_from_gameobject",
target="Player", # GameObject in scene
prefab_path="Assets/Prefabs/Player.prefab",
allow_overwrite=False
)
# Modify prefab contents (headless)
manage_prefabs(
action="modify_contents",
prefab_path="Assets/Prefabs/Player.prefab",
target="ChildObject", # object within prefab
position=[0, 1, 0],
components_to_add=["AudioSource"]
)
```
---
## Material & Shader Tools
### manage_material
Create and modify materials.
```python
# Create material
manage_material(
action="create",
material_path="Assets/Materials/Red.mat",
shader="Standard",
properties={"_Color": [1, 0, 0, 1]}
)
# Get material info
manage_material(action="get_material_info", material_path="Assets/Materials/Red.mat")
# Set shader property
manage_material(
action="set_material_shader_property",
material_path="Assets/Materials/Red.mat",
property="_Metallic",
value=0.8
)
# Set color
manage_material(
action="set_material_color",
material_path="Assets/Materials/Red.mat",
property="_BaseColor",
color=[0, 1, 0, 1] # RGBA
)
# Assign to renderer
manage_material(
action="assign_material_to_renderer",
target="MyCube",
material_path="Assets/Materials/Red.mat",
slot=0 # material slot index
)
# Set renderer color directly
manage_material(
action="set_renderer_color",
target="MyCube",
color=[1, 0, 0, 1],
mode="instance" # "shared"|"instance"|"property_block"
)
```
### manage_texture
Create procedural textures.
```python
manage_texture(
action="create",
path="Assets/Textures/Checker.png",
width=64,
height=64,
fill_color=[255, 255, 255, 255] # or [1.0, 1.0, 1.0, 1.0]
)
# Apply pattern
manage_texture(
action="apply_pattern",
path="Assets/Textures/Checker.png",
pattern="checkerboard", # "checkerboard"|"stripes"|"dots"|"grid"|"brick"
palette=[[0,0,0,255], [255,255,255,255]],
pattern_size=8
)
# Apply gradient
manage_texture(
action="apply_gradient",
path="Assets/Textures/Gradient.png",
gradient_type="linear", # "linear"|"radial"
gradient_angle=45,
palette=[[255,0,0,255], [0,0,255,255]]
)
```
---
## Editor Control Tools
### manage_editor
Control Unity Editor state.
```python
manage_editor(action="play") # Enter play mode
manage_editor(action="pause") # Pause play mode
manage_editor(action="stop") # Exit play mode
manage_editor(action="set_active_tool", tool_name="Move") # Move/Rotate/Scale/etc.
manage_editor(action="add_tag", tag_name="Enemy")
manage_editor(action="remove_tag", tag_name="OldTag")
manage_editor(action="add_layer", layer_name="Projectiles")
manage_editor(action="remove_layer", layer_name="OldLayer")
```
### execute_menu_item
Execute any Unity menu item.
```python
execute_menu_item(menu_path="File/Save Project")
execute_menu_item(menu_path="GameObject/3D Object/Cube")
execute_menu_item(menu_path="Window/General/Console")
```
### read_console
Read or clear Unity console messages.
```python
# Get recent messages
read_console(
action="get",
types=["error", "warning", "log"], # or ["all"]
count=10, # max messages (ignored with paging)
filter_text="NullReference", # optional text filter
since_timestamp="2024-01-01T00:00:00Z", # optional time filter
page_size=50,
cursor=0,
format="detailed", # "plain"|"detailed"|"json"
include_stacktrace=True
)
# Clear console
read_console(action="clear")
```
---
## Testing Tools
### run_tests
Start async test execution.
```python
result = run_tests(
mode="EditMode", # "EditMode"|"PlayMode"
test_names=["MyTests.TestA", "MyTests.TestB"], # specific tests
group_names=["Integration*"], # regex patterns
category_names=["Unit"], # NUnit categories
assembly_names=["Tests"], # assembly filter
include_failed_tests=True, # include failure details
include_details=False # include all test details
)
# Returns: {"job_id": "abc123", ...}
```
### get_test_job
Poll test job status.
```python
result = get_test_job(
job_id="abc123",
wait_timeout=60, # wait up to N seconds
include_failed_tests=True,
include_details=False
)
# Returns: {"status": "complete"|"running"|"failed", "results": {...}}
```
---
## Search Tools
### find_in_file
Search file contents with regex.
```python
find_in_file(
uri="mcpforunity://path/Assets/Scripts/MyScript.cs",
pattern="public void \\w+", # regex pattern
max_results=200,
ignore_case=True
)
# Returns: line numbers, content excerpts, match positions
```
---
## Custom Tools
### execute_custom_tool
Execute project-specific custom tools.
```python
execute_custom_tool(
tool_name="my_custom_tool",
parameters={"param1": "value", "param2": 42}
)
```
Discover available custom tools via `mcpforunity://custom-tools` resource.

View File

@ -0,0 +1,609 @@
# Unity-MCP Workflow Patterns
Common workflows and patterns for effective Unity-MCP usage.
## Table of Contents
- [Setup & Verification](#setup--verification)
- [Scene Creation Workflows](#scene-creation-workflows)
- [Script Development Workflows](#script-development-workflows)
- [Asset Management Workflows](#asset-management-workflows)
- [Testing Workflows](#testing-workflows)
- [Debugging Workflows](#debugging-workflows)
- [Batch Operations](#batch-operations)
---
## Setup & Verification
### Initial Connection Verification
```python
# 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
```
### Before Any Operation
```python
# 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
```
---
## Scene Creation Workflows
### Create Complete Scene from Scratch
```python
# 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_scene(action="screenshot")
# 6. Save scene
manage_scene(action="save")
```
### Populate Scene with Grid of Objects
```python
# 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)
```
### Clone and Arrange Objects
```python
# 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]
)
```
---
## Script Development Workflows
### Create New Script and Attach
```python
# 1. Create script
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. CRITICAL: Refresh and compile
refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True)
# 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
}
)
```
### Edit Existing Script Safely
```python
# 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. Refresh
refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True)
# 6. Check console
read_console(types=["error"], count=10)
```
### Add Method to Existing Class
```python
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;"
}
]
)
```
---
## Asset Management Workflows
### Create and Apply Material
```python
# 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_scene(action="screenshot")
```
### Create Procedural Texture
```python
# 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)
```
### Organize Assets into Folders
```python
# 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")
```
### Search and Process Assets
```python
# 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']}")
```
---
## Testing Workflows
### Run Specific Tests
```python
# 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 Tests by Category
```python
# 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
```
### Test-Driven Development Pattern
```python
# 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. Refresh
refresh_unity(mode="force", scope="scripts", compile="request", wait_for_ready=True)
# 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)
```
---
## Debugging Workflows
### Diagnose Compilation Errors
```python
# 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)
```
### Investigate Missing References
```python
# 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
)
```
### Check Scene State
```python
# 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_scene(action="screenshot")
```
---
## Batch Operations
### Mass Property Update
```python
# 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)
```
### Mass Object Creation with Variations
```python
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)
```
### Cleanup Pattern
```python
# 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)
```
---
## Error Recovery Patterns
### Stale File Recovery
```python
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"])
```
### Domain Reload Recovery
```python
# 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
```
### Compilation Block Recovery
```python
# 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
```