diff --git a/.claude/skills/unity-mcp-skill/SKILL.md b/.claude/skills/unity-mcp-skill/SKILL.md index 7c41a7fdad..8e3467b873 100644 --- a/.claude/skills/unity-mcp-skill/SKILL.md +++ b/.claude/skills/unity-mcp-skill/SKILL.md @@ -134,7 +134,7 @@ read_console( ) ``` -### 5. Always Check `editor_state` Before Complex Operations +### 5. Always Check `mcpforunity://editor/state` Before Complex Operations ```python # Read mcpforunity://editor/state to check: @@ -194,9 +194,11 @@ uri="file:///full/path/to/file.cs" | **Animation** | `manage_animation`, `manage_sprite` | Animator control, clips and controllers. **2D sprite sheets**: `manage_sprite(action="get_info")` returns a PNG or JPEG sheet as an image to count the grid from, then `full_setup` slices it and builds clips and a controller; clip names decide the states (idle, walk/run, attack-type triggers). Off by default over HTTP: `manage_tools(action="activate", group="animation")`. | | **Graphics** | `manage_graphics` | Rendering and post-processing management. 33 actions across 5 groups: **Volume** (create/configure volumes and effects, URP/HDRP), **Bake** (lightmaps, light probes, reflection probes, Edit mode only), **Stats** (draw calls, batches, memory), **Pipeline** (quality levels, pipeline settings), **Features** (URP renderer features: add, remove, toggle, reorder). Resources: `mcpforunity://scene/volumes`, `mcpforunity://rendering/stats`, `mcpforunity://pipeline/renderer-features`. Use `ping` to check pipeline status. See [tools-reference.md](references/tools-reference.md#graphics-tools). | | **Packages** | `manage_packages` | Install, remove, search, and manage Unity packages and scoped registries. Query actions: list installed, search registry, get info, ping, poll status. Mutating actions: add/remove packages, embed for editing, add/remove scoped registries, force resolve. Validates identifiers, warns on git URLs, checks dependents before removal (`force=true` to override). See [tools-reference.md](references/tools-reference.md#package-tools). | +| **Physics** | `manage_physics` | Manage 3D and 2D physics (21 actions). Settings, collision matrix, materials, joints (14 types). Queries: `raycast`, `raycast_all`, `linecast`, `shapecast` (sphere/box/capsule sweep), `overlap`. Forces: `apply_force` (AddForce/AddTorque/AddExplosionForce with ForceMode). Rigidbody: `get_rigidbody`, `configure_rigidbody` (mass, drag, gravity, constraints, collision detection). Validation: scene-wide checks. Simulation: `simulate_step` in edit mode. See [tools-reference.md](references/tools-reference.md#physics-tools). | | **ProBuilder** | `manage_probuilder` | 3D modeling, mesh editing, complex geometry. **When `com.unity.probuilder` is installed, prefer ProBuilder shapes over primitive GameObjects** for editable geometry, multi-material faces, or complex shapes. Supports 12 shape types, face/edge/vertex editing, smoothing, and per-face materials. See [ProBuilder Guide](references/probuilder-guide.md). | | **UI** | `manage_ui`, `batch_execute` with `manage_gameobject` + `manage_components` | **UI Toolkit**: Use `manage_ui` to create UXML/USS files, attach UIDocument, inspect visual trees. **uGUI (Canvas)**: Use `batch_execute` for Canvas, Panel, Button, Text, Slider, Toggle, Input Field. **Read `mcpforunity://project/info` first** to detect uGUI/TMP/Input System/UI Toolkit availability. (see [UI workflows](references/workflows.md#ui-creation-workflows)) | | **Docs** | `unity_reflect`, `unity_docs` | API verification and documentation lookup. **`unity_reflect`** inspects live C# APIs via reflection (requires Unity connection): `search` types across assemblies, `get_type` for member summary, `get_member` for full signatures. **`unity_docs`** fetches official docs from docs.unity3d.com (no Unity connection needed): `get_doc` (ScriptReference), `get_manual` (Manual pages), `get_package_doc` (package docs), `lookup` (parallel search all sources + project assets). **Trust hierarchy: reflection > project assets > docs.** Workflow: `unity_reflect` search -> get_type -> get_member -> `unity_docs` lookup. See [tools-reference.md](references/tools-reference.md#docs-tools). | +| **More** | `manage_material`, `manage_texture`, `manage_shader`, `manage_vfx`, `manage_profiler`, `execute_code`, `manage_scriptable_object`, `generate_image`, `generate_model`, `generate_audio`, `import_model`, `import_model_file`, `blender_bridge` | Materials; textures, shaders and VFX (`vfx` group); profiling (`profiling`); in-editor C# and ScriptableObjects (`scripting_ext`); AI asset generation, Sketchfab and on-disk model import, Blender (`asset_gen`). Every tool has a section in [tools-reference.md](references/tools-reference.md). | ## Common Workflows @@ -273,7 +275,7 @@ set_active_instance(instance="MyProject@abc123") | Symptom | Cause | Solution | |---------|-------|----------| -| Tools return "busy" | Compilation in progress | Wait, check `editor_state` | +| Tools return "busy" | Compilation in progress | Wait, check `mcpforunity://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` | @@ -282,6 +284,7 @@ set_active_instance(instance="MyProject@abc123") For detailed schemas and examples: -- **[tools-reference.md](references/tools-reference.md)**: Complete tool documentation with all parameters +- **[tools-reference.md](references/tools-reference.md)**: Every tool's main actions, parameters, and examples, with links to the full parameter tables - **[resources-reference.md](references/resources-reference.md)**: All available resources and their data - **[workflows.md](references/workflows.md)**: Extended workflow examples and patterns +- **[probuilder-guide.md](references/probuilder-guide.md)**: ProBuilder modeling patterns diff --git a/.claude/skills/unity-mcp-skill/references/probuilder-guide.md b/.claude/skills/unity-mcp-skill/references/probuilder-guide.md new file mode 100644 index 0000000000..d358d832e3 --- /dev/null +++ b/.claude/skills/unity-mcp-skill/references/probuilder-guide.md @@ -0,0 +1,447 @@ +# ProBuilder Workflow Guide + +Patterns and best practices for AI-driven ProBuilder mesh editing through MCP for Unity. + +## Availability + +ProBuilder is an **optional** Unity package (`com.unity.probuilder`). Check `mcpforunity://project/info` or call `manage_probuilder(action="ping")` to verify it's installed before using any ProBuilder tools. If available, **prefer ProBuilder over primitive GameObjects** for any geometry that needs editing, multi-material faces, or non-trivial shapes. + +## Core Workflow: Always Get Info First + +Before any mesh edit, call `get_mesh_info` with `include='faces'` to understand the geometry: + +```python +# Step 1: Get face info with directions +result = manage_probuilder(action="get_mesh_info", target="MyCube", + properties={"include": "faces"}) + +# Response includes per-face: +# index: 0, normal: [0, 1, 0], center: [0, 0.5, 0], direction: "top" +# index: 1, normal: [0, -1, 0], center: [0, -0.5, 0], direction: "bottom" +# index: 2, normal: [0, 0, 1], center: [0, 0, 0.5], direction: "front" +# ... + +# Step 2: Use the direction labels to pick faces +# Want to extrude the top? Find the face with direction="top" +manage_probuilder(action="extrude_faces", target="MyCube", + properties={"faceIndices": [0], "distance": 1.5}) +``` + +### Include Parameter + +| Value | Returns | Use When | +|-------|---------|----------| +| `"summary"` | Counts, bounds, materials | Quick check / validation | +| `"faces"` | + normals, centers, directions | Selecting faces for editing | +| `"edges"` | + edge vertex pairs with world positions (max 200) | Edge-based operations | +| `"all"` | Everything | Full mesh analysis | + +## Shape Creation + +### All 12 Shape Types + +```python +# Basic shapes +manage_probuilder(action="create_shape", properties={"shape_type": "Cube", "name": "MyCube"}) +manage_probuilder(action="create_shape", properties={"shape_type": "Sphere", "name": "MySphere"}) +manage_probuilder(action="create_shape", properties={"shape_type": "Cylinder", "name": "MyCyl"}) +manage_probuilder(action="create_shape", properties={"shape_type": "Plane", "name": "MyPlane"}) +manage_probuilder(action="create_shape", properties={"shape_type": "Cone", "name": "MyCone"}) +manage_probuilder(action="create_shape", properties={"shape_type": "Prism", "name": "MyPrism"}) + +# Parametric shapes +manage_probuilder(action="create_shape", properties={ + "shape_type": "Torus", "name": "MyTorus", + "rows": 16, "columns": 24, "innerRadius": 0.5, "outerRadius": 1.0 +}) +manage_probuilder(action="create_shape", properties={ + "shape_type": "Pipe", "name": "MyPipe", + "radius": 1.0, "height": 2.0, "thickness": 0.2 +}) +manage_probuilder(action="create_shape", properties={ + "shape_type": "Arch", "name": "MyArch", + "radius": 2.0, "angle": 180, "segments": 12 +}) + +# Architectural shapes +manage_probuilder(action="create_shape", properties={ + "shape_type": "Stair", "name": "MyStairs", "steps": 10 +}) +manage_probuilder(action="create_shape", properties={ + "shape_type": "CurvedStair", "name": "Spiral", "steps": 12 +}) +manage_probuilder(action="create_shape", properties={ + "shape_type": "Door", "name": "MyDoor" +}) + +# Custom polygon +manage_probuilder(action="create_poly_shape", properties={ + "points": [[0,0,0], [5,0,0], [5,0,5], [2.5,0,7], [0,0,5]], + "extrudeHeight": 3.0, "name": "Pentagon" +}) +``` + +## Common Editing Operations + +### Extrude a Roof + +```python +# 1. Create a building base +manage_probuilder(action="create_shape", properties={ + "shape_type": "Cube", "name": "Building", "size": [4, 3, 6] +}) + +# 2. Find the top face +info = manage_probuilder(action="get_mesh_info", target="Building", + properties={"include": "faces"}) +# Find face with direction="top" -> e.g. index 2 + +# 3. Extrude upward for a flat roof extension +manage_probuilder(action="extrude_faces", target="Building", + properties={"faceIndices": [2], "distance": 0.5}) +``` + +### Cut a Hole (Delete Faces) + +```python +# 1. Get face info +info = manage_probuilder(action="get_mesh_info", target="Wall", + properties={"include": "faces"}) +# Find the face with direction="front" -> e.g. index 4 + +# 2. Subdivide to create more faces +manage_probuilder(action="subdivide", target="Wall", + properties={"faceIndices": [4]}) + +# 3. Get updated face info (indices changed after subdivide!) +info = manage_probuilder(action="get_mesh_info", target="Wall", + properties={"include": "faces"}) + +# 4. Delete the center face(s) for the hole +manage_probuilder(action="delete_faces", target="Wall", + properties={"faceIndices": [6]}) +``` + +### Bevel Edges + +```python +# Get edge info +info = manage_probuilder(action="get_mesh_info", target="MyCube", + properties={"include": "edges"}) + +# Bevel specific edges +manage_probuilder(action="bevel_edges", target="MyCube", + properties={"edgeIndices": [0, 1, 2, 3], "amount": 0.1}) +``` + +### Detach Faces to New Object + +```python +# Detach and keep original (default) +manage_probuilder(action="detach_faces", target="MyCube", + properties={"faceIndices": [0, 1], "deleteSourceFaces": False}) + +# Detach and remove from source +manage_probuilder(action="detach_faces", target="MyCube", + properties={"faceIndices": [0, 1], "deleteSourceFaces": True}) +``` + +### Select Faces by Direction + +```python +# Select all upward-facing faces +manage_probuilder(action="select_faces", target="MyMesh", + properties={"direction": "up", "tolerance": 0.7}) + +# Grow selection from a seed face +manage_probuilder(action="select_faces", target="MyMesh", + properties={"growFrom": [0], "growAngle": 45}) +``` + +### Double-Sided Geometry + +```python +# Create inside faces for a room (duplicate and flip normals) +manage_probuilder(action="duplicate_and_flip", target="Room", + properties={"faceIndices": [0, 1, 2, 3, 4, 5]}) +``` + +### Create Polygon from Existing Vertices + +```python +# Connect existing vertices into a new face (auto-finds winding order) +manage_probuilder(action="create_polygon", target="MyMesh", + properties={"vertexIndices": [0, 3, 7, 4]}) +``` + +## Vertex Operations + +```python +# Move vertices by offset. Only the listed indices move: faces that meet at a corner +# usually each have their own vertex there, so moving one face's corners opens a gap. +manage_probuilder(action="move_vertices", target="MyCube", + properties={"vertexIndices": [0, 1, 2, 3], "offset": [0, 1, 0]}) + +# Weld nearby vertices (proximity-based merge) +manage_probuilder(action="weld_vertices", target="MyCube", + properties={"vertexIndices": [0, 1, 2, 3], "radius": 0.1}) + +# Insert vertex on an edge +manage_probuilder(action="insert_vertex", target="MyCube", + properties={"edge": {"a": 0, "b": 1}, "point": [0.5, 0, 0]}) + +# Add evenly-spaced points along edges +manage_probuilder(action="append_vertices_to_edge", target="MyCube", + properties={"edgeIndices": [0, 1], "count": 3}) +``` + +## Smoothing Workflow + +### Auto-Smooth (Recommended Default) + +```python +# Apply auto-smoothing with default 30 degree threshold +manage_probuilder(action="auto_smooth", target="MyMesh", + properties={"angleThreshold": 30}) +``` + +- **Low angle (15-25)**: More hard edges, faceted look +- **Medium angle (30-45)**: Good default, smooth curves + sharp corners +- **High angle (60-80)**: Very smooth, only sharpest edges remain hard + +### Manual Smoothing Groups + +```python +# Set specific faces to smooth group 1 +manage_probuilder(action="set_smoothing", target="MyMesh", + properties={"faceIndices": [0, 1, 2], "smoothingGroup": 1}) + +# Set other faces to hard edges (group 0) +manage_probuilder(action="set_smoothing", target="MyMesh", + properties={"faceIndices": [3, 4, 5], "smoothingGroup": 0}) +``` + +## Mesh Cleanup Pattern + +After editing, always clean up: + +```python +# 1. Center the pivot (important after extrusions that shift geometry) +manage_probuilder(action="center_pivot", target="MyMesh") + +# 2. Optionally freeze transform if you moved/rotated the object +manage_probuilder(action="freeze_transform", target="MyMesh") + +# 3. Validate mesh health +result = manage_probuilder(action="validate_mesh", target="MyMesh") +# Check result.data.healthy -- if false, repair + +# 4. Auto-repair if needed +manage_probuilder(action="repair_mesh", target="MyMesh") +``` + +## Building Complex Objects with ProBuilder + +When ProBuilder is available, prefer it over primitive GameObjects for complex geometry. ProBuilder lets you create, edit, and combine shapes into detailed objects without external 3D tools. + +### Example: Simple House + +```python +# 1. Create base building +manage_probuilder(action="create_shape", properties={ + "shape_type": "Cube", "name": "House", "width": 6, "height": 3, "depth": 8 +}) + +# 2. Get face info to find the top face +info = manage_probuilder(action="get_mesh_info", target="House", + properties={"include": "faces"}) +# Find direction="top" -> e.g. index 2 + +# 3. Extrude the top face to create a flat raised section +manage_probuilder(action="extrude_faces", target="House", + properties={"faceIndices": [2], "distance": 0.3}) + +# 4. Add a peaked roof as a separate Prism; its ridge runs along Z, like the house. +# (Moving the top face's vertices would pull it away from the walls; see Vertex Operations.) +# Shapes are centered on their position: the walls now reach y = 1.8, +# so a roof 2 high sits on them at y = 1.8 + 1. +manage_probuilder(action="create_shape", properties={ + "shape_type": "Prism", "name": "Roof", + "width": 6, "height": 2, "depth": 8, "position": [0, 2.8, 0] +}) + +# 5. Cut a doorway: subdivide front face, delete center sub-face +info = manage_probuilder(action="get_mesh_info", target="House", + properties={"include": "faces"}) +# Find direction="front", subdivide it +manage_probuilder(action="subdivide", target="House", + properties={"faceIndices": [4]}) + +# Re-query, find bottom-center face, delete it +info = manage_probuilder(action="get_mesh_info", target="House", + properties={"include": "faces"}) +manage_probuilder(action="delete_faces", target="House", + properties={"faceIndices": [12]}) + +# 6. Add a door frame with arch +manage_probuilder(action="create_shape", properties={ + "shape_type": "Door", "name": "Doorway", + "position": [0, 0, 4], "width": 1.5, "height": 2.5 +}) + +# 7. Add stairs to the door +manage_probuilder(action="create_shape", properties={ + "shape_type": "Stair", "name": "FrontSteps", + "position": [0, 0, 5], "steps": 3, "width": 2 +}) + +# 8. Smooth organic parts, keep architectural edges sharp +manage_probuilder(action="auto_smooth", target="House", + properties={"angleThreshold": 30}) + +# 9. Assign materials per face +manage_probuilder(action="set_face_material", target="House", + properties={"faceIndices": [0, 1, 2, 3], "materialPath": "Assets/Materials/Brick.mat"}) +# A Prism's faces 1 and 3 are its two slopes +manage_probuilder(action="set_face_material", target="Roof", + properties={"faceIndices": [1, 3], "materialPath": "Assets/Materials/Roof.mat"}) + +# 10. Cleanup +manage_probuilder(action="center_pivot", target="House") +manage_probuilder(action="validate_mesh", target="House") +``` + +### Example: Pillared Corridor (Batch) + +```python +# Create multiple columns efficiently +batch_execute(commands=[ + {"tool": "manage_probuilder", "params": { + "action": "create_shape", + "properties": {"shape_type": "Cylinder", "name": f"Pillar_{i}", + "radius": 0.3, "height": 4, "segments": 12, + "position": [i * 3, 0, 0]} + }} for i in range(6) +] + [ + # Floor + {"tool": "manage_probuilder", "params": { + "action": "create_shape", + "properties": {"shape_type": "Plane", "name": "Floor", + "width": 18, "height": 6, "position": [7.5, 0, 0]} + }}, + # Ceiling + {"tool": "manage_probuilder", "params": { + "action": "create_shape", + "properties": {"shape_type": "Plane", "name": "Ceiling", + "width": 18, "height": 6, "position": [7.5, 4, 0]} + }}, +]) + +# Bevel all pillar tops for decoration +for i in range(6): + info = manage_probuilder(action="get_mesh_info", target=f"Pillar_{i}", + properties={"include": "edges"}) + # Find top ring edges, bevel them + manage_probuilder(action="bevel_edges", target=f"Pillar_{i}", + properties={"edgeIndices": [0, 1, 2, 3], "amount": 0.05}) + +# Smooth the pillars +for i in range(6): + manage_probuilder(action="auto_smooth", target=f"Pillar_{i}", + properties={"angleThreshold": 45}) +``` + +### Example: Custom L-Shaped Room + +```python +# Use polygon shape for non-rectangular footprint +manage_probuilder(action="create_poly_shape", properties={ + "points": [ + [0, 0, 0], [10, 0, 0], [10, 0, 6], + [4, 0, 6], [4, 0, 10], [0, 0, 10] + ], + "extrudeHeight": 3.0, + "name": "LRoom" +}) + +# Create inside faces for the room interior +info = manage_probuilder(action="get_mesh_info", target="LRoom", + properties={"include": "faces"}) +# Duplicate and flip all faces to make interior visible +all_faces = list(range(info["data"]["faceCount"])) +manage_probuilder(action="duplicate_and_flip", target="LRoom", + properties={"faceIndices": all_faces}) + +# Cut a window: subdivide a wall face, delete center +# (follow the get_mesh_info -> subdivide -> get_mesh_info -> delete pattern) +``` + +### Example: Torus Knot / Decorative Ring + +```python +# Create a torus +manage_probuilder(action="create_shape", properties={ + "shape_type": "Torus", "name": "Ring", + "innerRadius": 0.3, "outerRadius": 2.0, + "rows": 24, "columns": 32 +}) + +# Smooth it for organic look +manage_probuilder(action="auto_smooth", target="Ring", + properties={"angleThreshold": 60}) + +# Assign metallic material +manage_probuilder(action="set_face_material", target="Ring", + properties={"faceIndices": [], "materialPath": "Assets/Materials/Gold.mat"}) +# Note: empty faceIndices = all faces +``` + +## Batch Patterns + +Use `batch_execute` for multi-step workflows to reduce round-trips: + +```python +batch_execute(commands=[ + {"tool": "manage_probuilder", "params": { + "action": "create_shape", + "properties": {"shape_type": "Cube", "name": "Column1", "position": [0, 0, 0]} + }}, + {"tool": "manage_probuilder", "params": { + "action": "create_shape", + "properties": {"shape_type": "Cube", "name": "Column2", "position": [5, 0, 0]} + }}, + {"tool": "manage_probuilder", "params": { + "action": "create_shape", + "properties": {"shape_type": "Cube", "name": "Column3", "position": [10, 0, 0]} + }}, +]) +``` + +## Known Limitations + +### Not Yet Working + +These actions exist in the API but have known bugs that prevent them from working correctly: + +| Action | Issue | Workaround | +|--------|-------|------------| +| `set_pivot` | Vertex positions don't persist through `ToMesh()`/`RefreshMesh()`. The `positions` property setter is overwritten when ProBuilder rebuilds the mesh. Needs `SetVertices(IList)` or direct `m_Positions` field access. | Use `center_pivot` instead, or position objects via Transform. | +| `convert_to_probuilder` | `MeshImporter` constructor throws internally. May need ProBuilder's editor-only `ProBuilderize` API instead of runtime `MeshImporter`. | Create shapes natively with `create_shape` or `create_poly_shape` instead of converting existing meshes. | + +### General Limitations + +- Face indices are **not stable** across edits -- always re-query `get_mesh_info` after any modification +- Edge data is capped at **200 edges** in `get_mesh_info` results +- Face data is capped at **100 faces** in `get_mesh_info` results +- `subdivide` uses `ConnectElements.Connect` internally (ProBuilder has no public `Subdivide` API), which connects face midpoints rather than traditional quad subdivision + +## Key Rules + +1. **Always get_mesh_info before editing** -- face indices are not stable across edits +2. **Re-query after modifications** -- subdivide, extrude, delete all change face indices +3. **Use direction labels** -- don't guess face indices, use the direction field +4. **Cleanup after editing** -- center_pivot + validate is good practice +5. **Auto-smooth for organic shapes** -- 30 degrees is a good default +6. **Prefer ProBuilder over primitives** -- when the package is available and you need editable geometry +7. **Use batch_execute** -- for creating multiple shapes or repetitive operations +8. **Screenshot to verify** -- use `manage_camera(action="screenshot", include_image=True)` to check visual results after complex edits diff --git a/.claude/skills/unity-mcp-skill/references/resources-reference.md b/.claude/skills/unity-mcp-skill/references/resources-reference.md new file mode 100644 index 0000000000..ddd4fda90c --- /dev/null +++ b/.claude/skills/unity-mcp-skill/references/resources-reference.md @@ -0,0 +1,662 @@ +# 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) +- [Camera Resources](#camera-resources) +- [Graphics Resources](#graphics-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`, `pipeline`, `rendering`, `menu-items`, `custom-tools`, `tool-groups`, `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 +} +``` + +--- + +## Camera Resources + +### mcpforunity://scene/cameras + +**Purpose:** List all cameras in the scene (Unity Camera + CinemachineCamera) with full status. Read this before using `manage_camera` to understand the current camera setup. + +**Returns:** +```json +{ + "brain": { + "exists": true, + "gameObject": "Main Camera", + "instanceID": 55504, + "activeCameraName": "Cam_Cinematic", + "activeCameraID": -39420, + "isBlending": false + }, + "cinemachineCameras": [ + { + "instanceID": -39420, + "name": "Cam_Cinematic", + "isLive": true, + "priority": 50, + "follow": {"name": "CameraTarget", "instanceID": -26766}, + "lookAt": {"name": "CameraTarget", "instanceID": -26766}, + "body": "CinemachineThirdPersonFollow", + "aim": "CinemachineRotationComposer", + "noise": "CinemachineBasicMultiChannelPerlin", + "extensions": [] + } + ], + "unityCameras": [ + { + "instanceID": 55504, + "name": "Main Camera", + "depth": 0.0, + "fieldOfView": 50.0, + "hasBrain": true + } + ], + "cinemachineInstalled": true +} +``` + +**Key Fields:** +- `brain`: CinemachineBrain status — which camera is active, blend state +- `cinemachineCameras`: All CinemachineCamera components with pipeline info (body, aim, noise, extensions) +- `unityCameras`: All Unity Camera components with depth and FOV +- `cinemachineInstalled`: Whether Cinemachine package is available + +**Use with:** `manage_camera` tool for creating/configuring cameras + +--- + +## Graphics Resources + +### mcpforunity://scene/volumes + +**Purpose:** List all Volume components in the scene with effects and parameters. Read this before using `manage_graphics` volume actions. + +**Returns:** +```json +{ + "pipeline": "Universal (URP)", + "volumes": [ + { + "name": "PostProcessVolume", + "instance_id": -24600, + "is_global": true, + "weight": 1.0, + "priority": 0, + "blend_distance": 0, + "profile": "MyProfile", + "profile_path": "Assets/Settings/MyProfile.asset", + "effects": [ + { + "type": "Bloom", + "active": true, + "overridden_params": ["intensity", "threshold", "scatter"] + }, + { + "type": "Vignette", + "active": true, + "overridden_params": ["intensity", "smoothness"] + } + ] + } + ] +} +``` + +**Key Fields:** +- `is_global`: Whether the volume applies everywhere or only within its collider bounds +- `effects[].overridden_params`: Which parameters are actively overridden (not using defaults) +- `profile_path`: Empty string for embedded profiles, asset path for shared profiles + +**Use with:** `manage_graphics` volume actions (volume_create, volume_add_effect, volume_set_effect, etc.) + +### mcpforunity://rendering/stats + +**Purpose:** Current rendering performance counters (draw calls, batches, triangles, memory). + +**Returns:** +```json +{ + "draw_calls": 42, + "batches": 35, + "set_pass_calls": 12, + "triangles": 15234, + "vertices": 8456, + "dynamic_batches": 5, + "static_batches": 20, + "shadow_casters": 3, + "render_textures": 8, + "render_textures_bytes": 16777216, + "visible_skinned_meshes": 2 +} +``` + +**Use with:** `manage_graphics` stats actions (stats_get, stats_list_counters, stats_get_memory) + +### mcpforunity://pipeline/renderer-features + +**Purpose:** URP renderer features on the active renderer (SSAO, Decals, etc.). + +**Returns:** +```json +{ + "rendererDataName": "PC_Renderer", + "features": [ + { + "index": 0, + "name": "ScreenSpaceAmbientOcclusion", + "type": "ScreenSpaceAmbientOcclusion", + "isActive": true, + "properties": { "m_Settings": "Generic" } + } + ] +} +``` + +**Key Fields:** +- `index`: Position in the feature list (use for feature_toggle, feature_remove, feature_configure) +- `isActive`: Whether the feature is enabled +- `rendererDataName`: Which URP renderer data asset is active + +**Use with:** `manage_graphics` feature actions (feature_list, feature_add, feature_remove, feature_toggle, etc.) + +--- + +## 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** (abbreviated: one of the 3 tools is shown): +```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} + ] + } + ] +} +``` + +### mcpforunity://tool-groups + +**Purpose:** Every tool group, whether it is on by default, and its tools. Enable or hide a group for the session with `manage_tools`. + +**Returns** (abbreviated: `groups` has one entry per group, in name order; one is shown): +```json +{ + "groups": [ + {"name": "animation", "description": "Animator control, AnimationClip creation & 2D sprite-sheet animation", + "default_enabled": false, "tools": ["manage_animation", "manage_sprite"], "tool_count": 2} + ], + "total_groups": 10, + "default_enabled": ["core"], + "usage": "Call manage_tools(action='activate', group='') to enable a group." +} +``` + +--- + +## 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 +``` diff --git a/.claude/skills/unity-mcp-skill/references/tools-reference.md b/.claude/skills/unity-mcp-skill/references/tools-reference.md index 601e533a93..2ff79a1186 100644 --- a/.claude/skills/unity-mcp-skill/references/tools-reference.md +++ b/.claude/skills/unity-mcp-skill/references/tools-reference.md @@ -1,6 +1,8 @@ # Unity-MCP Tools Reference -Complete reference for all MCP tools. Each tool includes parameters, types, and usage examples. +Every built-in MCP tool, with its main actions, parameters, and usage examples. For a tool's full parameter table, open its generated page: `https://coplaydev.github.io/unity-mcp/reference/tools//` (tools with no group, such as `manage_tools`, are under `core`). + +Each section names the tool's group when it is not `core`. A tool outside `core` can be hidden (over HTTP, only `core` starts enabled); enable its group with `manage_tools(action="activate", group="")`. > **Template warning:** Examples in this file are skill templates and may be inaccurate for some Unity versions, packages, or project setups. Validate parameters and payload shapes against your active tool schema and runtime behavior. @@ -14,7 +16,10 @@ Complete reference for all MCP tools. Each tool includes parameters, types, and - [Material & Shader Tools](#material--shader-tools) - [UI Tools](#ui-tools) - [Editor Control Tools](#editor-control-tools) +- [Build Tools](#build-tools) - [Testing Tools](#testing-tools) +- [Search Tools](#search-tools) +- [Custom Tools](#custom-tools) - [Camera Tools](#camera-tools) - [Graphics Tools](#graphics-tools) - [Package Tools](#package-tools) @@ -22,6 +27,10 @@ Complete reference for all MCP tools. Each tool includes parameters, types, and - [ProBuilder Tools](#probuilder-tools) - [Profiler Tools](#profiler-tools) - [Docs Tools](#docs-tools) +- [Animation Tools](#animation-tools) +- [VFX Tools](#vfx-tools) +- [Scripting Extension Tools](#scripting-extension-tools) +- [Asset Generation Tools](#asset-generation-tools) --- @@ -98,13 +107,36 @@ refresh_unity( ) ``` +### manage_tools + +Show, enable, or hide tool groups for the current session. Always visible (no group). + +```python +manage_tools(action="list_groups") # every group, whether it is on, and its tools +manage_tools(action="activate", group="animation") # "deactivate" hides a group again +manage_tools(action="sync") # re-read the tool toggles in the Unity editor +manage_tools(action="reset") # back to the defaults +``` + +Groups: `core`, `animation`, `asset_gen`, `docs`, `probuilder`, `profiling`, `scripting_ext`, `testing`, `ui`, `vfx`. The `mcpforunity://tool-groups` resource lists the same catalog read-only. [Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/manage_tools) + +### debug_request_context + +Diagnostics: returns the request's `client_id`, `session_id` and meta, the session's active Unity instance, and the server version. No parameters; always visible (no group). + +```python +debug_request_context() +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/debug_request_context) + --- ## Scene Tools ### manage_scene -Scene CRUD operations, hierarchy queries, screenshots, and scene view control. +Scene CRUD operations, hierarchy queries, multi-scene editing, validation, and scene view control. Screenshots are `manage_camera` actions (see [Camera Tools](#camera-tools)); build-settings scenes are `manage_build(action="scenes")`. ```python # Get hierarchy (paginated) @@ -116,55 +148,6 @@ manage_scene( include_transform=False # bool - include local transforms ) -# Screenshot (file only — saves to Assets/Screenshots/) -manage_camera(action="screenshot") - -# Screenshot with inline image (base64 PNG returned to AI) -manage_scene( - action="screenshot", - camera="MainCamera", # str, optional - camera name, path, or instance ID - include_image=True, # bool, default False - return base64 PNG inline - max_resolution=512 # int, optional - downscale cap (default 640) -) - -# Batch surround — contact sheet of 6 fixed angles (front/back/left/right/top/bird_eye) -manage_scene( - action="screenshot", - batch="surround", # str - "surround" for 6-angle contact sheet - max_resolution=256 # int - per-tile resolution cap -) -# Returns: single composite contact sheet image with labeled tiles - -# Batch surround centered on a specific target -manage_scene( - action="screenshot", - batch="surround", - view_target="Player", # str|int|list[float] - center surround on this target - max_resolution=256 -) - -# Batch orbit — configurable multi-angle grid around a target -manage_scene( - action="screenshot", - batch="orbit", # str - "orbit" for configurable angle grid - view_target="Player", # str|int|list[float] - target to orbit around - orbit_angles=8, # int, default 8 - number of azimuth steps - orbit_elevations=[0, 30], # list[float], default [0, 30, -15] - vertical angles in degrees - orbit_distance=10, # float, optional - camera distance (auto-fit if omitted) - orbit_fov=60, # float, default 60 - camera FOV in degrees - max_resolution=256 # int - per-tile resolution cap -) -# Returns: single composite contact sheet (angles × elevations tiles in a grid) - -# Positioned screenshot (temp camera at viewpoint, no file saved) -manage_scene( - action="screenshot", - view_target="Enemy", # str|int|list[float] - target to aim at - view_position=[0, 10, -10], # list[float], optional - camera position - view_rotation=[45, 0, 0], # list[float], optional - euler angles (overrides view_target aim) - max_resolution=512 -) - # Frame scene view on target manage_scene( action="scene_view_frame", @@ -177,6 +160,20 @@ 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") + +# Scene templates: "empty" (no objects), "default", "3d_basic", "2d_basic" +manage_scene(action="create", name="Level1", path="Assets/Scenes/", template="3d_basic") + +# Multi-scene editing +manage_scene(action="load", path="Assets/Scenes/Level2.unity", additive=True) # keep current scene +manage_scene(action="get_loaded_scenes") +manage_scene(action="set_active_scene", scene_name="Level2") +manage_scene(action="move_to_scene", target="Player", scene_name="Level2") # moves a root GameObject +manage_scene(action="close_scene", scene_name="Level2", remove_scene=True) # remove_scene=False only unloads + +# Validation: missing scripts, broken prefabs +manage_scene(action="validate") +manage_scene(action="validate", auto_repair=True) # also removes missing scripts (undoable) ``` ### find_gameobjects @@ -334,6 +331,8 @@ manage_components( # - "Assets/Prefabs/My.prefab" → String shorthand for asset paths # - "ObjectName" → String shorthand for scene name lookup # - 12345 → Integer shorthand for instanceID +# - {"guid": "...", "spriteName": "Hero_0"} or {"guid": "...", "fileID": 12345} +# → Sprite sub-asset of a sliced sheet (single-sprite textures resolve from guid/path alone) ``` --- @@ -444,7 +443,7 @@ Get file hash without content (for preconditions). ```python get_sha(uri="mcpforunity://path/Assets/Scripts/MyScript.cs") -# Returns: {"sha256": "...", "lengthBytes": 1234, "lastModifiedUtc": "..."} +# Returns: {"sha256": "...", "lengthBytes": 1234} ``` ### delete_script @@ -455,6 +454,26 @@ Delete a script file. delete_script(uri="mcpforunity://path/Assets/Scripts/OldScript.cs") ``` +### manage_script + +Legacy whole-file router with three actions: `create`, `read`, `delete`. `name` is the class name without `.cs`; `path` is its folder under `Assets/`. There is no update action: edit with `script_apply_edits` or `apply_text_edits`. + +```python +manage_script(action="read", name="PlayerController", path="Assets/Scripts") # returns the file contents +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/manage_script) + +### manage_script_capabilities + +Lists the edit ops `script_apply_edits` supports (structured and text), the max edit payload size, and the active guards. No parameters; always visible (no group). + +```python +manage_script_capabilities() +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/manage_script_capabilities) + --- ## Asset Tools @@ -500,7 +519,7 @@ manage_asset(action="delete", path="Assets/OldAsset.asset") ### manage_prefabs -Headless prefab operations. +Prefab assets: headless edits with `modify_contents`, or interactive editing in a prefab stage. ```python # Get prefab info @@ -539,6 +558,8 @@ manage_prefabs( prefab_path="Assets/Prefabs/Player.prefab", create_child={"name": "SpawnPoint", "primitive_type": "Sphere", "position": [0, 2, 0]} ) +# A list creates several children in one save; "source_prefab_path" instead of +# "primitive_type" nests a prefab instance: {"name": "Gun", "source_prefab_path": "Assets/Prefabs/Gun.prefab"} # Set component properties on prefab contents manage_prefabs( @@ -547,6 +568,11 @@ manage_prefabs( target="ChildObject", component_properties={"Rigidbody": {"mass": 5.0}, "MyScript": {"health": 100}} ) + +# Interactive: open a prefab stage, edit with manage_gameobject/manage_components, save, close +manage_prefabs(action="open_prefab_stage", prefab_path="Assets/Prefabs/Enemy.prefab") +manage_prefabs(action="save_prefab_stage") +manage_prefabs(action="close_prefab_stage") # back to the main scene ``` --- @@ -607,7 +633,7 @@ manage_material( ### manage_texture -Create procedural textures. +Create procedural textures. Group: `vfx`. ```python manage_texture( @@ -637,13 +663,24 @@ manage_texture( ) ``` +### manage_shader + +Create, read, update, or delete `.shader` files. Group: `vfx`. `name` is the file name without `.shader`; `path` is its folder under `Assets/`. To use the shader, pass the name declared in its `Shader "..."` line to `manage_material`. + +```python +manage_shader(action="create", name="UnlitTint", path="Assets/Shaders", + contents='Shader "Custom/UnlitTint" { ... }') +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/vfx/manage_shader) + --- ## UI Tools ### manage_ui -Manage Unity UI Toolkit elements: UXML documents, USS stylesheets, UIDocument components, and visual tree inspection. +Manage Unity UI Toolkit elements: UXML documents, USS stylesheets, UIDocument components, and visual tree inspection. Group: `ui`. ```python # Create a UXML file @@ -715,7 +752,7 @@ manage_ui( ### manage_editor -Control Unity Editor state. +Control Unity Editor state, undo/redo. Prefab stages are `manage_prefabs` actions. ```python manage_editor(action="play") # Enter play mode @@ -730,9 +767,8 @@ manage_editor(action="remove_tag", tag_name="OldTag") manage_editor(action="add_layer", layer_name="Projectiles") manage_editor(action="remove_layer", layer_name="OldLayer") -manage_editor(action="open_prefab_stage", prefab_path="Assets/Prefabs/Enemy.prefab") -manage_editor(action="save_prefab_stage") # Save changes in the open prefab stage -manage_editor(action="close_prefab_stage") # Exit prefab editing mode back to main scene +manage_editor(action="undo") # Returns the name of the undone step +manage_editor(action="redo") # Package deployment (no confirmation dialog — designed for LLM-driven iteration) manage_editor(action="deploy_package") # Copy configured MCPForUnity source into installed package @@ -774,8 +810,26 @@ read_console(action="clear") --- +## Build Tools + +### manage_build + +Player builds, platform switching, player settings, build-settings scenes, build profiles, and batch builds. Actions: `build`, `status`, `platform`, `settings`, `scenes`, `profiles`, `batch`, `cancel`. `build` and `batch` return a `job_id`; poll it with `status`, stop it with `cancel`. `platform`, `settings` (with `property`) and `scenes` read when the value is omitted and write when it is given. `profiles` and `profile` need Unity 6+. + +```python +manage_build(action="scenes", scenes='["Assets/Scenes/Menu.unity", "Assets/Scenes/Level1.unity"]') # replaces the list +manage_build(action="build", target="windows64", output_path="Builds/Win/Game.exe", development="true") +manage_build(action="status", job_id="") +``` + +Targets: `windows64`, `osx`, `linux64`, `android`, `ios`, `webgl`, `uwp`, `tvos`, `visionos`. Switching `platform` reimports assets. [Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/manage_build) + +--- + ## Testing Tools +Group: `testing`. + ### run_tests Start async test execution. @@ -964,6 +1018,19 @@ manage_camera(action="screenshot", capture_source="scene_view", view_target="Can # Multi-view screenshot (6-angle contact sheet) manage_camera(action="screenshot_multiview", max_resolution=480) +# Batch surround centered on a target: one contact sheet of 6 labeled angles +manage_camera(action="screenshot", batch="surround", view_target="Player", max_resolution=256) + +# Batch orbit: grid of orbit_angles x orbit_elevations tiles around a target +manage_camera(action="screenshot", batch="orbit", view_target="Player", + orbit_angles=8, # azimuth steps (default 8, max 36) + orbit_elevations=[0, 30], # degrees (default [0, 30, -15]) + orbit_distance=10, # optional, auto-fit if omitted + orbit_fov=60, max_resolution=256) + +# Positioned capture: temp camera at a viewpoint (view_rotation overrides the view_target aim) +manage_camera(action="screenshot", view_target="Enemy", view_position=[0, 10, -10], max_resolution=512) + # List all cameras manage_camera(action="list_cameras") ``` @@ -1313,7 +1380,7 @@ manage_physics(action="simulate_step", steps=10, step_size=0.02) ### manage_probuilder -Unified tool for ProBuilder mesh operations. Requires `com.unity.probuilder` package. When available, **prefer ProBuilder over primitive GameObjects** for editable geometry, multi-material faces, or complex shapes. +Unified tool for ProBuilder mesh operations. Group: `probuilder`. Requires `com.unity.probuilder` package. When available, **prefer ProBuilder over primitive GameObjects** for editable geometry, multi-material faces, or complex shapes. **Parameters:** @@ -1582,3 +1649,158 @@ unity_docs(action="lookup", queries="Physics.Raycast,NavMeshAgent,Light2D") unity_docs(action="lookup", query="VolumeProfile", package="com.unity.render-pipelines.universal", pkg_version="17.0") ``` + +--- + +## Animation Tools + +Group: `animation`. + +### manage_animation + +Animator control, AnimatorController authoring, and AnimationClip creation. Actions by prefix: + +- `animator_*` (a scene object's Animator): `get_info`, `get_parameter`, `play`, `crossfade`, `set_parameter`, `set_speed`, `set_enabled` +- `controller_*` (`.controller` assets): `create`, `get_info`, `assign`, `add_state`, `add_transition`, `add_parameter`, `add_layer`, `remove_layer`, `set_layer_weight`, `create_blend_tree_1d`, `create_blend_tree_2d`, `add_blend_tree_child` +- `clip_*` (`.anim` assets): `create`, `get_info`, `add_curve`, `set_curve`, `set_vector_curve`, `create_preset`, `assign`, `add_event`, `remove_event` + +Top-level parameters are `target`, `search_method`, `clip_path` and `controller_path`; everything else goes in `properties` (camelCase or snake_case keys). + +```python +manage_animation(action="controller_add_transition", controller_path="Assets/Animators/Player.controller", + properties={"fromState": "Idle", "toState": "Walk", "hasExitTime": False, + "conditions": [{"parameter": "Speed", "mode": "greater", "threshold": 0.1}]}) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/animation/manage_animation) + +### manage_sprite + +Turns a 2D sprite sheet into sliced sprites, AnimationClips and an AnimatorController. Actions: `get_info`, `slice_sheet`, `setup_clips`, `setup_controller`, `full_setup`. + +- `get_info` returns the sheet's import settings and slices and, for PNG/JPEG, the sheet as an image: count the grid from it before slicing. +- `slice_sheet` cuts a grid (`cols`/`rows` or `frame_width`/`frame_height`) and replaces the sheet's existing slices; `filter_mode` defaults to `point`. +- `full_setup` runs slice, clips, controller. Clip names decide the controller: `idle` is the default state, `walk`/`run` share a `Speed` blend tree, `jump`/`attack`/`hurt`-type names become trigger states. +- Without `overwrite=True`, an existing clip is skipped and an existing controller fails the call. + +```python +manage_sprite(action="full_setup", path="Assets/Sprites/hero.png", cols=6, rows=2, + clips=[{"name": "idle", "start_frame": 0, "end_frame": 5}, + {"name": "walk", "start_frame": 6, "end_frame": 11}], + add_to_scene=True, scene_target="Hero") +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/animation/manage_sprite) + +--- + +## VFX Tools + +### manage_vfx + +ParticleSystem, VisualEffect (VFX Graph), LineRenderer and TrailRenderer components. Group: `vfx`. Action prefixes: `particle_*` (create, modules, bursts, playback), `vfx_*` (VFX Graph assets, exposed properties, events; needs `com.unity.visualeffectgraph`), `line_*` (positions, width, color, and shapes such as `line_create_circle`), `trail_*`; plus `ping`. Settings go in `properties` (camelCase or snake_case keys); `component_index` picks one of several components of the same type. + +```python +manage_vfx(action="particle_create", target="Sparks", properties={"position": [0, 1, 0]}) # creates the GameObject if missing +manage_vfx(action="particle_set_main", target="Sparks", + properties={"startLifetime": 0.8, "startSpeed": 6, "startSize": 0.1, "maxParticles": 200}) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/vfx/manage_vfx) + +--- + +## Scripting Extension Tools + +Group: `scripting_ext`. + +### execute_code + +Runs a C# method body inside the editor, compiled in memory (no script file). `System`, `System.Collections.Generic`, `System.Linq`, `System.Reflection`, `UnityEngine` and `UnityEditor` are imported; `return` a value to get it back. Actions: `execute`, `get_history`, `replay` (`index`), `clear_history`. `safety_checks` (default on) blocks known dangerous calls but is not a sandbox. `compiler`: `auto` (Roslyn if installed, else CodeDom with C# 6), `roslyn`, `codedom`. + +```python +execute_code(action="execute", code="return Selection.gameObjects.Select(g => g.name).ToArray();") +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/scripting_ext/execute_code) + +### manage_scriptable_object + +Creates ScriptableObject assets and edits their serialized fields by property path. `create` takes `type_name` (namespace-qualified), `folder_path`, `asset_name` and optional `overwrite`; `modify` takes `target` (`{"path": ...}` or `{"guid": ...}`) and optional `dry_run`. Both apply `patches`: `{"propertyPath": ..., "op": "set" (default) or "array_resize", "value": ...}`; an object reference uses `"ref": {"guid": ...}` in place of `"value"`. + +```python +manage_scriptable_object(action="modify", target={"path": "Assets/Data/Goblin.asset"}, + patches=[{"propertyPath": "maxHealth", "value": 50}, + {"propertyPath": "drops", "op": "array_resize", "value": 2}]) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/scripting_ext/manage_scriptable_object) + +--- + +## Asset Generation Tools + +Group: `asset_gen`. Bring your own key: provider keys are entered in the Unity editor (MCP for Unity window, Generative tab) and never cross the bridge; `list_providers` shows which are configured. Generation and Sketchfab imports are async: `generate`/`import` return a `job_id`, `status` reports progress and then the imported `assetPath`, and `cancel` stops the job. + +### generate_image + +Text-to-image or image-to-image with fal.ai or OpenRouter, imported as a texture or sprite. Actions: `generate`, `status`, `cancel`, `list_providers`, `list_models`, `refresh_models` (`remove_background` is not supported yet). `generate` takes `provider` (`fal`, `openrouter`), `mode` (`text` or `image`), `prompt` or `image_path`/`image_url`, and optional `model` (from `list_models`), `width`, `height`, `transparent`, `name`, `output_folder`. + +```python +generate_image(action="generate", provider="fal", mode="text", prompt="mossy stone wall, hand-painted", + width=1024, height=1024, output_folder="Assets/Generated") +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/generate_image) + +### generate_model + +Text-to-3D or image-to-3D with Tripo, Meshy or fal, imported as a model. Actions as `generate_image`, without `remove_background`. `generate` takes `provider` (`tripo`, `meshy`, `fal`; fal outputs GLB), `mode`, `prompt` or an image, and optional `format` (`glb`, `fbx`, `obj`, `usdz`), `target_size` (meters, largest side), `texture`, `tier`, `model`. + +```python +generate_model(action="generate", provider="tripo", mode="text", prompt="low-poly treasure chest", + format="glb", target_size=1.0) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/generate_model) + +### generate_audio + +Sound effects and music from a text prompt with fal.ai, imported as an AudioClip. Actions as `generate_model`. `generate` takes `prompt` and optional `model` and `duration` (seconds, clamped per model). + +```python +generate_audio(action="generate", provider="fal", prompt="short bright coin pickup chime", duration=2) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/generate_audio) + +### import_model + +Search Sketchfab and import a downloadable model. Actions: `search` (`query`, `categories`, `downloadable`, `count`, `cursor`), `preview` (`uid`: license, face counts, thumbnails), `import` (`uid`, `target_size`, `name`, `output_folder`), `status`, `cancel`, `list_providers`. + +```python +import_model(action="search", query="wooden barrel", downloadable=True, count=5) +import_model(action="import", uid="", target_size=1.0) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/import_model) + +### import_model_file + +Imports a model file already on disk (`.fbx`, `.obj`, `.glb`, `.gltf`, or a `.zip` holding a model and its sidecar files) through the import pipeline the generators use. No `action` parameter and no key. A rigged FBX/OBJ needs `animation_type="generic"` or `"humanoid"`, or it imports without clips; glTF needs the glTFast package. + +```python +import_model_file(source_path="C:/Exports/robot.fbx", animation_type="humanoid", target_size=1.8) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/import_model_file) + +### blender_bridge + +Drives a running Blender that has the BlenderMCP addon connected (default `127.0.0.1:9876`) directly from Unity. Actions: `status`, `scene_info`, `object_info`, `screenshot`, `run_python`, `import_model`, `compare_screenshot`, `setup_bloom`, `check_updates`, `sync_addon`. `import_model` exports from Blender (glb by default), imports the file, places it at `position`, and scales its largest side to `target_size` meters. + +```python +blender_bridge(action="import_model", object_names=["Robot"], target_size=2.0, position=[0, 0, 0]) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/blender_bridge) diff --git a/.claude/skills/unity-mcp-skill/references/workflows.md b/.claude/skills/unity-mcp-skill/references/workflows.md index 9df5ffbfcf..1bb196f840 100644 --- a/.claude/skills/unity-mcp-skill/references/workflows.md +++ b/.claude/skills/unity-mcp-skill/references/workflows.md @@ -107,17 +107,18 @@ batch_execute(commands=[ ]) ``` -### Script Overwrites with `manage_script(action="update")` +### Script Overwrites with `replace_class` -When a generated script needs to be rewritten (e.g., to add auto-wiring logic), use `update` instead of deleting and recreating: +When a generated script needs to be rewritten (e.g., to add auto-wiring logic), replace its class in place instead of deleting and recreating the file: a recreated script gets a new GUID, so components already using it lose their reference. `create_script` refuses to overwrite an existing file, and `manage_script` has no update action. ```python -manage_script( - action="update", - path="Assets/Scripts/MyScript.cs", - contents="using UnityEngine;\n\npublic class MyScript : MonoBehaviour { ... }" +script_apply_edits( + name="MyScript", + path="Assets/Scripts", + edits=[{"op": "replace_class", "className": "MyScript", + "replacement": "public class MyScript : MonoBehaviour { ... }"}] ) -# manage_script update auto-triggers import + compile — just wait and check console +# script_apply_edits auto-triggers import + compile — just wait and check console # Read mcpforunity://editor/state → wait until is_compiling == false read_console(types=["error"], count=10) ``` @@ -1905,6 +1906,8 @@ refresh_unity(mode="force", compile="request", wait_for_ready=True) ## API Verification Workflows +> These tools live in the opt-in `docs` group. Activate it first: `manage_tools(action="activate", group="docs")` + ### Full API Verification Before Writing Code Use `unity_reflect` and `unity_docs` to verify Unity APIs before writing C# code. This prevents hallucinated or outdated API references. @@ -1934,7 +1937,7 @@ unity_docs(action="get_doc", class_name="NavMeshAgent", member_name="SetDestinat Use `unity_docs` `lookup` action to search multiple APIs in a single call: ```python -# Search ScriptReference + Manual + package docs in parallel +# 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 diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 8d3619cc26..341cdf77be 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -10,13 +10,23 @@ on: paths: - Server/** - tools/** - - .github/workflows/python-tests.yml + # Files outside Server/ and tools/ that the tests read: the tool manifest, both + # skill copies (kept identical by test_skill_copies_in_sync.py) and the workflows. + - manifest.json + - unity-mcp-skill/** + - .claude/skills/** + - .github/workflows/** pull_request: branches: [main, beta] paths: - Server/** - tools/** - - .github/workflows/python-tests.yml + # Files outside Server/ and tools/ that the tests read: the tool manifest, both + # skill copies (kept identical by test_skill_copies_in_sync.py) and the workflows. + - manifest.json + - unity-mcp-skill/** + - .claude/skills/** + - .github/workflows/** workflow_dispatch: {} workflow_call: inputs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a7e7aa4e0a..feb96d9fc1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -454,6 +454,23 @@ jobs: gh pr view "$PR_NUMBER" --json state,mergeStateStatus,isDraft -q '{state: .state, mergeStateStatus: .mergeStateStatus, isDraft: .isDraft}' exit 1 + sync_release_notes: + name: Dispatch release-notes sync + needs: + - bump + runs-on: ubuntu-latest + # The bump job creates the release with GITHUB_TOKEN, and events made with that token + # start no workflows, so sync-releases.yml never sees `release: published`. + # workflow_dispatch is exempt from that rule. + permissions: + actions: write + steps: + - name: Dispatch sync-releases.yml on beta + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: gh workflow run sync-releases.yml --repo "$GITHUB_REPOSITORY" --ref beta + publish_docker: name: Publish Docker image needs: diff --git a/.github/workflows/sync-releases.yml b/.github/workflows/sync-releases.yml index 8892af0b05..edfb64abc8 100644 --- a/.github/workflows/sync-releases.yml +++ b/.github/workflows/sync-releases.yml @@ -4,11 +4,14 @@ name: Docs — Sync Release Notes # in sync with the GitHub Releases API. Source of truth is GitHub Releases. # # Triggers (intentionally narrow): -# - release.published / edited / unpublished / deleted: the only time -# the synced files can legitimately go stale. Fires on every release -# event so the docs reflect the new version within ~30 seconds. -# - workflow_dispatch: manual escape hatch (re-run after a one-off -# GitHub UI edit to a release body, or to backfill after a downtime). +# - workflow_dispatch: release.yml dispatches this right after it creates +# a release. That release is made with GITHUB_TOKEN, and events made +# with GITHUB_TOKEN start no workflows (workflow_dispatch and +# repository_dispatch are the exceptions), so the release trigger below +# never fires for it. Also the manual escape hatch (re-run after a +# one-off GitHub UI edit to a release body, or to backfill). +# - release.published / edited / unpublished / deleted: releases that a +# person creates or edits in the GitHub UI. # # Why no pull_request / schedule triggers: # - PR-time drift-check would fail outsider PRs that touched README for @@ -21,9 +24,13 @@ name: Docs — Sync Release Notes # workflow_dispatch handle the rare out-of-band UI edit. # # Behavior: -# - Commits directly to `beta` with [skip ci] in the message — the -# change is mechanical (re-render from API output), pre-validated, -# and confined to two well-known files. +# - The `beta` ruleset requires a pull request, so the job pushes a +# branch, opens a PR into beta and merges it, like beta-release.yml. +# The change is mechanical (re-render from API output) and confined +# to two well-known files. +# - That merge is made with GITHUB_TOKEN too, so it does not start +# "Docs — Build & Deploy"; the site shows the new releases.md after +# the next push to beta that deploys it. on: release: @@ -43,6 +50,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + pull-requests: write steps: - name: Checkout beta uses: actions/checkout@v4 @@ -50,7 +58,7 @@ jobs: ref: beta # Full history so the commit lands on a fresh ref tip. fetch-depth: 0 - # `sync` needs to push back, so the token must persist here. + # The PR step pushes a branch, so the token must persist here. persist-credentials: true token: ${{ secrets.GITHUB_TOKEN }} @@ -64,15 +72,25 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: python tools/sync_release_notes.py - - name: Commit & push (if anything changed) + - name: Open and merge a PR into beta (if anything changed) + env: + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail if git diff --quiet -- website/docs/releases.md README.md; then echo "No drift — release notes already up to date." exit 0 fi - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + BRANCH="docs/release-notes-${GITHUB_RUN_ID}" + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git checkout -b "$BRANCH" git add website/docs/releases.md README.md - git commit -m "docs: sync release notes from GitHub Releases [skip ci]" - git push origin beta + git commit -m "docs: sync release notes from GitHub Releases" + git push origin "$BRANCH" + PR_URL=$(gh pr create \ + --base beta \ + --head "$BRANCH" \ + --title "docs: sync release notes from GitHub Releases" \ + --body "Automated re-render of website/docs/releases.md and the README's Recent Updates block from the GitHub Releases API.") + gh pr merge "$PR_URL" --merge --delete-branch diff --git a/.gitignore b/.gitignore index ea1e3e3350..e3d220f9d6 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,10 @@ scripts/local-test/ tools/.unity-check-logs/ # Ignore the .claude directory, since it might contain local/project-level setting such as deny and allowlist. -/.claude +# Except .claude/skills: the Editor installs the unity-mcp skill from there, and while the whole +# directory was ignored, `git add` skipped new skill files without a word. +/.claude/* +!/.claude/skills/ .mcp.json # Superpowers skill working artifacts (specs, plans, SDD ledger) diff --git a/README.md b/README.md index 8a487f0d53..40b7780cac 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@
Recent Updates -* **[v10.0.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.0.0)** (2026-06-30) -* **[v9.7.3](https://github.com/CoplayDev/unity-mcp/releases/tag/v9.7.3)** (2026-06-15) -* **[v9.7.1](https://github.com/CoplayDev/unity-mcp/releases/tag/v9.7.1)** (2026-05-24) -* **[v9.7.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v9.7.0)** (2026-05-22) -* **[v9.6.8](https://github.com/CoplayDev/unity-mcp/releases/tag/v9.6.8)** (2026-04-27) +* **[v10.3.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.3.0)** (2026-10-04) +* **[v10.2.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.2.0)** (2026-09-01) +* **[v10.1.2](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.1.2)** (2026-08-02) +* **[v10.1.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.1.0)** (2026-07-13) +* **[v10.0.2](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.0.2)** (2026-07-13) Full history: [Release Notes](https://coplaydev.github.io/unity-mcp/releases). diff --git a/Server/src/cli/CLI_USAGE_GUIDE.md b/Server/src/cli/CLI_USAGE_GUIDE.md index fe330a061d..84a5aeb83d 100644 --- a/Server/src/cli/CLI_USAGE_GUIDE.md +++ b/Server/src/cli/CLI_USAGE_GUIDE.md @@ -12,6 +12,7 @@ 6. [Common Mistakes and Corrections](#common-mistakes-and-corrections) 7. [Output Formats](#output-formats) 8. [Command Reference by Category](#command-reference-by-category) +9. [Complete Command Reference](#complete-command-reference) --- @@ -150,7 +151,7 @@ Global options come **BEFORE** the command group: | `--format` | `-f` | Output format: `text`, `json`, `table` | `text` | | `--timeout` | `-t` | Command timeout in seconds | `30` | | `--instance` | `-i` | Target Unity instance (hash or Name@hash) | auto | -| `--verbose` | `-v` | Enable verbose output | `false` | +| `--verbose` | `-v` | Print each command sent to Unity and its raw response to stderr | `false` | **✅ Correct:** ```bash @@ -288,14 +289,15 @@ When targeting GameObjects, specify how to search: # By name (default) unity-mcp gameobject modify "Player" --position 0 0 0 -# By instance ID (use --search-method) -unity-mcp gameobject modify "-81840" --search-method by_id --position 0 0 0 +# By instance ID (use --search-method). IDs are often negative, and a bare -81840 +# reads as an option ("No such option: -8"): put options first, then -- before the ID +unity-mcp gameobject modify --search-method by_id --position 0 0 0 -- -81840 # By path unity-mcp gameobject modify "/Canvas/Panel/Button" --search-method by_path --active -# By tag -unity-mcp gameobject find "Player" --search-method by_tag +# By tag (gameobject find takes --method, not --search-method) +unity-mcp gameobject find "Player" --method by_tag ``` --- @@ -346,6 +348,19 @@ unity-mcp status unity-mcp instances ``` +### Instance Commands + +```bash +# List connected Unity instances +unity-mcp instance list + +# Target one instance: per call with --instance, or for a whole shell with UNITY_MCP_INSTANCE +unity-mcp --instance "ProjectName@abc123" editor play + +# Show current instance +unity-mcp instance current +``` + ### Scene Commands ```bash @@ -378,7 +393,7 @@ unity-mcp camera screenshot --file-name "my_screenshot" --super-size 2 # Find GameObjects unity-mcp gameobject find "Player" unity-mcp gameobject find "Enemy" --method by_tag -unity-mcp gameobject find "-81840" --method by_id +unity-mcp gameobject find --method by_id -- -81840 # -- before a negative ID unity-mcp gameobject find "Rigidbody" --method by_component # Create GameObject @@ -474,6 +489,22 @@ unity-mcp script delete "Assets/Scripts/MyScript.cs" unity-mcp script validate "Assets/Scripts/MyScript.cs" ``` +### Shader Commands + +```bash +# Create shader +unity-mcp shader create "MyShader" --path "Assets/Shaders" + +# Read shader +unity-mcp shader read "Assets/Shaders/Custom.shader" + +# Update from file +unity-mcp shader update "Assets/Shaders/Custom.shader" --file local.shader + +# Delete shader +unity-mcp shader delete "Assets/Shaders/Old.shader" --force +``` + ### Material Commands ```bash @@ -511,7 +542,7 @@ unity-mcp editor stop unity-mcp editor console # Read console unity-mcp editor console --count 20 # Last 20 entries unity-mcp editor console --clear # Clear console -unity-mcp editor console --types error,warning # Filter by type +unity-mcp editor console --type error --type warning # Filter: error, warning, log or all # Menu items unity-mcp editor menu "Edit/Preferences" @@ -545,6 +576,65 @@ unity-mcp editor custom-tool "MyBuildTool" unity-mcp editor custom-tool "Deploy" --params '{"target": "Android"}' ``` +### VFX Commands + +VFX Graph tooling is tested against com.unity.visualeffectgraph 12.1.13 with URP/HDRP; other versions may be unsupported. + +```bash +# Particle systems +unity-mcp vfx particle info "Fire" +unity-mcp vfx particle play "Fire" --with-children +unity-mcp vfx particle stop "Fire" + +# Line renderers +unity-mcp vfx line info "LaserBeam" +unity-mcp vfx line create-line "Line" --start 0 0 0 --end 10 5 0 +unity-mcp vfx line create-circle "Circle" --radius 5 + +# Trail renderers +unity-mcp vfx trail info "PlayerTrail" +unity-mcp vfx trail set-time "Trail" 2.0 + +# Raw VFX actions (access all 60+ actions) +unity-mcp vfx raw particle_set_main "Fire" --params '{"duration": 5}' +``` + +### ProBuilder Commands + +Requires the com.unity.probuilder package in your Unity project. + +```bash +# Create shapes +unity-mcp probuilder create-shape Cube +unity-mcp probuilder create-shape Torus --name "MyTorus" --params '{"rows": 16, "columns": 16}' +unity-mcp probuilder create-shape Stair --position 0 0 5 --params '{"steps": 10}' + +# Create from polygon footprint +unity-mcp probuilder create-poly --points "[[0,0,0],[5,0,0],[5,0,5],[0,0,5]]" --height 3 + +# Get mesh info +unity-mcp probuilder info "MyCube" + +# Raw ProBuilder actions +unity-mcp probuilder raw extrude_faces "MyCube" --params '{"faceIndices": [0], "distance": 1.0}' +unity-mcp probuilder raw bevel_edges "MyCube" --params '{"edgeIndices": [0,1], "amount": 0.2}' +unity-mcp probuilder raw set_face_material "MyCube" --params '{"faceIndices": [0], "materialPath": "Assets/Materials/Red.mat"}' +``` + +### Batch Commands + +```bash +# Execute from JSON file +unity-mcp batch run commands.json +unity-mcp batch run commands.json --parallel --fail-fast + +# Execute inline JSON +unity-mcp batch inline '[{"tool": "manage_scene", "params": {"action": "get_active"}}]' + +# Generate template +unity-mcp batch template > my_commands.json +``` + ### Prefab Commands ```bash @@ -913,6 +1003,52 @@ unity-mcp raw manage_packages '{"action": "list_packages"}' --- +## Complete Command Reference + +| Group | Subcommands | +|-------|-------------| +| (top level) | `instances`, `raw`, `status` | +| `animation` | `raw` | +| `animation animator` | `crossfade`, `get-parameter`, `info`, `play`, `set-enabled`, `set-parameter`, `set-speed` | +| `animation clip` | `add-curve`, `add-event`, `assign`, `create`, `create-preset`, `info`, `remove-event`, `set-curve`, `set-vector-curve` | +| `animation controller` | `add-blend-tree-child`, `add-layer`, `add-parameter`, `add-state`, `add-transition`, `assign`, `create`, `create-blend-tree-1d`, `create-blend-tree-2d`, `info`, `remove-layer`, `set-layer-weight` | +| `asset` | `create`, `delete`, `duplicate`, `import`, `info`, `mkdir`, `move`, `rename`, `search` | +| `asset-gen` | `generate-audio`, `generate-image`, `generate-model`, `import-model`, `import-model-file`, `list-models`, `status` | +| `audio` | `play`, `stop`, `volume` | +| `batch` | `inline`, `run`, `template` | +| `blender` | `check-updates`, `compare-screenshot`, `import-model`, `object-info`, `run-python`, `scene-info`, `screenshot`, `setup-bloom`, `status`, `sync-addon` | +| `build` | `batch`, `cancel`, `platform`, `profiles`, `run`, `scenes`, `settings`, `status` | +| `camera` | `add-extension`, `brain-status`, `create`, `ensure-brain`, `force`, `list`, `ping`, `release`, `remove-extension`, `screenshot`, `screenshot-multiview`, `set-aim`, `set-blend`, `set-body`, `set-lens`, `set-noise`, `set-priority`, `set-target` | +| `code` | `clear-history`, `execute`, `history`, `read`, `replay`, `search` | +| `component` | `add`, `modify`, `remove`, `set` | +| `custom_tool` | `list` | +| `docs` | `get` | +| `editor` | `add-layer`, `add-tag`, `console`, `custom-tool`, `deploy`, `menu`, `pause`, `play`, `poll-test`, `redo`, `refresh`, `remove-layer`, `remove-tag`, `restore`, `stop`, `tests`, `tool`, `undo` | +| `gameobject` | `create`, `delete`, `duplicate`, `find`, `modify`, `move` | +| `graphics` | `bake-cancel`, `bake-clear`, `bake-create-probes`, `bake-create-reflection`, `bake-reflection-probe`, `bake-set-settings`, `bake-settings`, `bake-start`, `bake-status`, `feature-add`, `feature-configure`, `feature-list`, `feature-remove`, `feature-reorder`, `feature-toggle`, `ping`, `pipeline-info`, `pipeline-set-quality`, `pipeline-set-settings`, `pipeline-settings`, `skybox-info`, `skybox-set-ambient`, `skybox-set-fog`, `skybox-set-material`, `skybox-set-properties`, `skybox-set-reflection`, `skybox-set-sun`, `stats`, `stats-debug-mode`, `stats-memory`, `volume-add-effect`, `volume-create`, `volume-create-profile`, `volume-info`, `volume-list-effects`, `volume-remove-effect`, `volume-set-effect`, `volume-set-properties` | +| `instance` | `current`, `list` | +| `lighting` | `create` | +| `material` | `assign`, `create`, `info`, `set-color`, `set-property`, `set-renderer-color` | +| `packages` | `add`, `add-registry`, `embed`, `info`, `list`, `list-registries`, `ping`, `remove`, `remove-registry`, `resolve`, `search`, `status` | +| `physics` | `add-joint`, `apply-force`, `assign-material`, `configure-joint`, `configure-material`, `configure-rigidbody`, `create-material`, `get-collision-matrix`, `get-rigidbody`, `get-settings`, `linecast`, `overlap`, `ping`, `raycast`, `raycast-all`, `remove-joint`, `set-collision-matrix`, `set-settings`, `shapecast`, `simulate`, `validate` | +| `prefab` | `close`, `create`, `hierarchy`, `info`, `modify`, `open`, `save` | +| `probuilder` | `auto-smooth`, `bevel-edges`, `center-pivot`, `create-poly`, `create-shape`, `delete-faces`, `extrude-edges`, `extrude-faces`, `freeze-transform`, `info`, `move-vertices`, `raw`, `repair`, `select-faces`, `set-material`, `set-pivot`, `set-smoothing`, `subdivide`, `validate`, `weld-vertices` | +| `profiler` | `frame-debugger-disable`, `frame-debugger-enable`, `frame-debugger-events`, `frame-timing`, `get-counters`, `memory-compare`, `memory-list`, `memory-snapshot`, `object-memory`, `set-areas`, `start`, `status`, `stop` | +| `reflect` | `member`, `search`, `type` | +| `scene` | `active`, `build-settings`, `close`, `create`, `hierarchy`, `load`, `loaded`, `move-to`, `open-additive`, `save`, `set-active`, `validate` | +| `script` | `create`, `delete`, `edit`, `read`, `validate` | +| `shader` | `create`, `delete`, `read`, `update` | +| `sprite` | `full-setup`, `info`, `setup-clips`, `setup-controller`, `slice` | +| `texture` | `create`, `delete`, `modify`, `set-import-settings`, `sprite` | +| `tool` | `list` | +| `ui` | `create-button`, `create-canvas`, `create-image`, `create-text` | +| `vfx` | `raw` | +| `vfx line` | `clear`, `create-circle`, `create-line`, `info`, `set-positions` | +| `vfx particle` | `clear`, `info`, `pause`, `play`, `restart`, `stop` | +| `vfx trail` | `clear`, `info`, `set-time` | + +--- + ## Known Behaviors ### Component Creation @@ -1000,7 +1136,7 @@ unity-mcp [GLOBAL_OPTIONS] command subcommand [ARGS] [OPTIONS] unity-mcp gameobject modify --help ``` -2. **Use verbose mode** to see what's happening: +2. **Use verbose mode** to see each command sent to Unity and its raw response (on stderr): ```bash unity-mcp -v scene hierarchy diff --git a/Server/src/cli/commands/animation.py b/Server/src/cli/commands/animation.py index a9e6f566ca..4116c2c543 100644 --- a/Server/src/cli/commands/animation.py +++ b/Server/src/cli/commands/animation.py @@ -58,7 +58,7 @@ def animator_info(target: str, search_method: Optional[str]): \b Examples: unity-mcp animation animator info "Player" - unity-mcp animation animator info "-12345" --search-method by_id + unity-mcp animation animator info --search-method by_id -- -12345 """ config = get_config() params: dict[str, Any] = {"action": "animator_get_info", "target": target} diff --git a/Server/src/cli/commands/batch.py b/Server/src/cli/commands/batch.py index f4be5ced57..f7f61a14a3 100644 --- a/Server/src/cli/commands/batch.py +++ b/Server/src/cli/commands/batch.py @@ -27,7 +27,7 @@ def batch_run(file: str, parallel: bool, fail_fast: bool): The JSON file should contain an array of command objects with 'tool' and 'params' keys. - \\b + \b File format: [ {"tool": "manage_gameobject", "params": {"action": "create", "name": "Cube1"}}, @@ -35,7 +35,7 @@ def batch_run(file: str, parallel: bool, fail_fast: bool): {"tool": "manage_components", "params": {"action": "add", "target": "Cube1", "componentType": "Rigidbody"}} ] - \\b + \b Examples: unity-mcp batch run commands.json unity-mcp batch run setup.json --parallel @@ -92,7 +92,7 @@ def batch_run(file: str, parallel: bool, fail_fast: bool): def batch_inline(commands_json: str, parallel: bool, fail_fast: bool): """Execute commands from inline JSON. - \\b + \b Examples: unity-mcp batch inline '[{"tool": "manage_scene", "params": {"action": "get_active"}}]' @@ -124,7 +124,7 @@ def batch_inline(commands_json: str, parallel: bool, fail_fast: bool): def batch_template(output: Optional[str]): """Generate a sample batch commands file. - \\b + \b Examples: unity-mcp batch template > commands.json unity-mcp batch template -o my_batch.json diff --git a/Server/src/cli/commands/camera.py b/Server/src/cli/commands/camera.py index e2d7f98c2b..6859467802 100644 --- a/Server/src/cli/commands/camera.py +++ b/Server/src/cli/commands/camera.py @@ -6,7 +6,7 @@ from cli.utils.config import get_config from cli.utils.output import format_output, print_error, print_success from cli.utils.connection import run_command, handle_unity_errors -from cli.utils.parsers import parse_json_dict_or_exit +from cli.utils.parsers import parse_json_dict_or_exit, parse_json_list_or_exit from cli.utils.constants import SEARCH_METHOD_CHOICE_BASIC @@ -519,8 +519,11 @@ def screenshot(camera_ref, file_name, super_size, include_image, max_resolution, params["captureSource"] = capture_source if batch: params["batch"] = batch + # Unity reads a JSON array as a position and any other value as a GameObject; the + # option arrives as text, so "[x,y,z]" is parsed to reach Unity as an array. if view_target: - params["viewTarget"] = view_target + params["viewTarget"] = (parse_json_list_or_exit(view_target, "view-target") + if view_target.lstrip().startswith("[") else view_target) if output_folder: params["outputFolder"] = output_folder result = run_command("manage_camera", params, config) @@ -529,7 +532,7 @@ def screenshot(camera_ref, file_name, super_size, include_image, max_resolution, @camera.command("screenshot-multiview") @click.option("--max-resolution", type=int, default=None, help="Max resolution per tile.") -@click.option("--view-target", default=None, help="Center target for the multiview capture.") +@click.option("--view-target", default=None, help="Center target: GameObject name/path/ID or [x,y,z].") @click.option("--output-folder", default=None, help="Output folder, project-relative or absolute inside the project. " "Overrides Editor preference; falls back to Assets/Screenshots when unset.") @@ -547,7 +550,8 @@ def screenshot_multiview(max_resolution, view_target, output_folder): if max_resolution is not None: params["maxResolution"] = max_resolution if view_target: - params["viewTarget"] = view_target + params["viewTarget"] = (parse_json_list_or_exit(view_target, "view-target") + if view_target.lstrip().startswith("[") else view_target) if output_folder: params["outputFolder"] = output_folder result = run_command("manage_camera", params, config) diff --git a/Server/src/cli/commands/code.py b/Server/src/cli/commands/code.py index 1e2763aa35..3fdaccfc0f 100644 --- a/Server/src/cli/commands/code.py +++ b/Server/src/cli/commands/code.py @@ -176,7 +176,7 @@ def search(pattern: str, path: str, max_results: int, case_sensitive: bool): PATTERN is a regex pattern to search for. PATH is the script path (e.g., Assets/Scripts/Player.cs). - \\b + \b Examples: unity-mcp code search "class.*Player" "Assets/Scripts/Player.cs" unity-mcp code search "private.*int" "Assets/Scripts/GameManager.cs" diff --git a/Server/src/cli/commands/component.py b/Server/src/cli/commands/component.py index 7460e0d5af..0eb6d5d52a 100644 --- a/Server/src/cli/commands/component.py +++ b/Server/src/cli/commands/component.py @@ -40,7 +40,7 @@ def add(target: str, component_type: str, search_method: Optional[str], properti \b Examples: unity-mcp component add "Player" Rigidbody - unity-mcp component add "-81840" BoxCollider --search-method by_id + unity-mcp component add --search-method by_id -- -81840 BoxCollider unity-mcp component add "Enemy" Rigidbody --properties '{"mass": 5.0, "useGravity": true}' """ config = get_config() @@ -89,7 +89,7 @@ def remove(target: str, component_type: str, search_method: Optional[str], force \b Examples: unity-mcp component remove "Player" Rigidbody - unity-mcp component remove "-81840" BoxCollider --search-method by_id --force + unity-mcp component remove --search-method by_id --force -- -81840 BoxCollider unity-mcp component remove "Player" BoxCollider --component-index 1 """ config = get_config() @@ -138,7 +138,7 @@ def set_property(target: str, component_type: str, property_name: str, value: st Examples: unity-mcp component set "Player" Rigidbody mass 5.0 unity-mcp component set "Enemy" Transform position "[0, 5, 0]" - unity-mcp component set "-81840" Light intensity 2.5 --search-method by_id + unity-mcp component set --search-method by_id -- -81840 Light intensity 2.5 unity-mcp component set "Player" BoxCollider size "[2,2,2]" --component-index 1 """ config = get_config() diff --git a/Server/src/cli/commands/gameobject.py b/Server/src/cli/commands/gameobject.py index 301e6a7165..9d36968a56 100644 --- a/Server/src/cli/commands/gameobject.py +++ b/Server/src/cli/commands/gameobject.py @@ -51,7 +51,7 @@ def find(search_term: str, method: str, include_inactive: bool, limit: int, curs Examples: unity-mcp gameobject find "Player" unity-mcp gameobject find "Enemy" --method by_tag - unity-mcp gameobject find "-81840" --method by_id + unity-mcp gameobject find --method by_id -- -81840 unity-mcp gameobject find "Rigidbody" --method by_component unity-mcp gameobject find "/Canvas/Panel" --method by_path """ @@ -291,7 +291,7 @@ def modify( Examples: unity-mcp gameobject modify "Player" --position 0 5 0 unity-mcp gameobject modify "Enemy" --name "Boss" --tag "Boss" - unity-mcp gameobject modify "-81840" --search-method by_id --active + unity-mcp gameobject modify --search-method by_id --active -- -81840 unity-mcp gameobject modify "/Canvas/Panel" --search-method by_path --inactive unity-mcp gameobject modify "Cube" --add-components "Rigidbody,BoxCollider" unity-mcp gameobject modify "Ground" --static @@ -352,7 +352,7 @@ def delete(target: str, search_method: Optional[str], force: bool): \b Examples: unity-mcp gameobject delete "OldObject" - unity-mcp gameobject delete "-81840" --search-method by_id + unity-mcp gameobject delete --search-method by_id -- -81840 unity-mcp gameobject delete "TempObjects" --search-method by_tag --force """ config = get_config() @@ -406,7 +406,7 @@ def duplicate( Examples: unity-mcp gameobject duplicate "Player" unity-mcp gameobject duplicate "Enemy" --name "Enemy2" --offset 5 0 0 - unity-mcp gameobject duplicate "-81840" --search-method by_id + unity-mcp gameobject duplicate --search-method by_id -- -81840 """ config = get_config() diff --git a/Server/src/cli/commands/instance.py b/Server/src/cli/commands/instance.py index bd39ede6e8..2156f90193 100644 --- a/Server/src/cli/commands/instance.py +++ b/Server/src/cli/commands/instance.py @@ -4,13 +4,13 @@ from typing import Optional from cli.utils.config import get_config -from cli.utils.output import format_output, print_error, print_success, print_info -from cli.utils.connection import run_command, run_list_instances, handle_unity_errors +from cli.utils.output import print_info +from cli.utils.connection import run_list_instances, handle_unity_errors @click.group() def instance(): - """Unity instance management - list, select, and view instances.""" + """Unity instance management - list the connected instances and show the one in use.""" pass @@ -19,7 +19,7 @@ def instance(): def list_instances(): """List available Unity instances. - \\b + \b Examples: unity-mcp instance list """ @@ -47,36 +47,11 @@ def list_instances(): click.echo(f" Session: {session_id[:8]}...") -@instance.command("set") -@click.argument("instance_id") -@handle_unity_errors -def set_instance(instance_id: str): - """Set the active Unity instance. - - INSTANCE_ID can be Name@hash or just a hash prefix. - - \\b - Examples: - unity-mcp instance set "MyProject@abc123" - unity-mcp instance set abc123 - """ - config = get_config() - - result = run_command("set_active_instance", { - "instance": instance_id, - }, config) - click.echo(format_output(result, config.format)) - if result.get("success"): - data = result.get("data", {}) - active = data.get("instance", instance_id) - print_success(f"Active instance set to: {active}") - - @instance.command("current") def current_instance(): """Show the currently selected Unity instance. - \\b + \b Examples: unity-mcp instance current """ @@ -90,4 +65,4 @@ def current_instance(): print_info( "No instance explicitly set. Using default (auto-select single instance).") print_info("Use 'unity-mcp instance list' to see available instances.") - print_info("Use 'unity-mcp instance set ' to select one.") + print_info("Choose one per call with --instance , or for a whole shell with UNITY_MCP_INSTANCE.") diff --git a/Server/src/cli/commands/material.py b/Server/src/cli/commands/material.py index 53c8376fb1..431f384b08 100644 --- a/Server/src/cli/commands/material.py +++ b/Server/src/cli/commands/material.py @@ -173,7 +173,7 @@ def assign(material_path: str, target: str, search_method: Optional[str], slot: Examples: unity-mcp material assign "Assets/Materials/Red.mat" "Cube" unity-mcp material assign "Assets/Materials/Blue.mat" "Player" --mode instance - unity-mcp material assign "Assets/Materials/Mat.mat" "-81840" --search-method by_id --slot 1 + unity-mcp material assign --search-method by_id --slot 1 -- "Assets/Materials/Mat.mat" -81840 """ config = get_config() diff --git a/Server/src/cli/commands/probuilder.py b/Server/src/cli/commands/probuilder.py index 0a61cbd2e0..c9fcc38bf5 100644 --- a/Server/src/cli/commands/probuilder.py +++ b/Server/src/cli/commands/probuilder.py @@ -64,10 +64,11 @@ def probuilder(): def create_shape(shape_type: str, name: Optional[str], position, rotation, params: str): """Create a ProBuilder shape with real dimensions. - \\b + \b Shape types: Cube, Cylinder, Sphere, Plane, Cone, Torus, Pipe, Arch, Stair, CurvedStair, Door, Prism + \b Each shape accepts type-specific dimension parameters: Cube/Prism: width, height, depth (or size for uniform) Cylinder: radius, height, segments/axisDivisions, heightCuts @@ -81,7 +82,7 @@ def create_shape(shape_type: str, name: Optional[str], position, rotation, param Arch: radius, width, depth, angle, radialCuts Door: width, height, depth, ledgeHeight, legWidth - \\b + \b Examples: unity-mcp probuilder create-shape Cube unity-mcp probuilder create-shape Cube --params '{"width": 2, "height": 3, "depth": 1}' @@ -119,7 +120,7 @@ def create_shape(shape_type: str, name: Optional[str], position, rotation, param def create_poly(points: str, height: float, name: Optional[str], flip_normals: bool): """Create a ProBuilder mesh from a 2D polygon footprint. - \\b + \b Examples: unity-mcp probuilder create-poly --points "[[0,0,0],[5,0,0],[5,0,5],[0,0,5]]" --height 3 """ @@ -158,7 +159,7 @@ def extrude_faces(target: str, faces: str, distance: float, method: str, search_method: Optional[str]): """Extrude faces of a ProBuilder mesh. - \\b + \b Examples: unity-mcp probuilder extrude-faces "MyCube" --faces '[0]' --distance 1.0 unity-mcp probuilder extrude-faces "MyCube" --faces '[0,1,2]' --method IndividualFaces @@ -194,11 +195,11 @@ def extrude_edges(target: str, edges: str, distance: float, as_group: bool, search_method: Optional[str]): """Extrude edges of a ProBuilder mesh. - \\b + \b Edges can be specified as flat indices into the unique edge list, or as vertex pairs [{a: 0, b: 1}, ...]. - \\b + \b Examples: unity-mcp probuilder extrude-edges "MyCube" --edges '[0,1]' --distance 0.5 unity-mcp probuilder extrude-edges "MyCube" --edges '[{"a":0,"b":1}]' --distance 1 @@ -232,7 +233,7 @@ def extrude_edges(target: str, edges: str, distance: float, as_group: bool, def bevel_edges(target: str, edges: str, amount: float, search_method: Optional[str]): """Bevel edges of a ProBuilder mesh. - \\b + \b Examples: unity-mcp probuilder bevel-edges "MyCube" --edges '[0,1,2]' --amount 0.2 unity-mcp probuilder bevel-edges "MyCube" --edges '[{"a":0,"b":1}]' --amount 0.15 @@ -263,7 +264,7 @@ def bevel_edges(target: str, edges: str, amount: float, search_method: Optional[ def delete_faces(target: str, faces: str, search_method: Optional[str]): """Delete faces from a ProBuilder mesh. - \\b + \b Examples: unity-mcp probuilder delete-faces "MyCube" --faces '[0,1]' """ @@ -292,7 +293,7 @@ def delete_faces(target: str, faces: str, search_method: Optional[str]): def subdivide(target: str, faces: Optional[str], search_method: Optional[str]): """Subdivide faces of a ProBuilder mesh. - \\b + \b Examples: unity-mcp probuilder subdivide "MyCube" unity-mcp probuilder subdivide "MyCube" --faces '[0,1]' @@ -328,7 +329,7 @@ def select_faces(target: str, direction: Optional[str], tolerance: float, search_method: Optional[str]): """Select faces by criteria (direction, grow, flood, loop). - \\b + \b Examples: unity-mcp probuilder select-faces "MyCube" --direction up unity-mcp probuilder select-faces "MyCube" --direction forward --tolerance 0.9 @@ -364,7 +365,7 @@ def select_faces(target: str, direction: Optional[str], tolerance: float, def move_vertices(target: str, vertices: str, offset, search_method: Optional[str]): """Move vertices by an offset. - \\b + \b Examples: unity-mcp probuilder move-vertices "MyCube" --vertices '[0,1,2,3]' --offset 0 1 0 """ @@ -396,7 +397,7 @@ def weld_vertices(target: str, vertices: str, radius: float, search_method: Optional[str]): """Weld vertices within a proximity radius. - \\b + \b Examples: unity-mcp probuilder weld-vertices "MyCube" --vertices '[0,1,2,3]' --radius 0.1 """ @@ -428,7 +429,7 @@ def set_material(target: str, faces: str, material: str, search_method: Optional[str]): """Assign a material to specific faces. - \\b + \b Examples: unity-mcp probuilder set-material "MyCube" --faces '[0,1]' --material "Assets/Materials/Red.mat" """ @@ -463,14 +464,14 @@ def set_material(target: str, faces: str, material: str, def mesh_info(target: str, include: str, search_method: Optional[str]): """Get ProBuilder mesh info. - \\b + \b Edge data now includes world-space vertex positions and uses deduplicated edges. - \\b + \b Examples: unity-mcp probuilder info "MyCube" unity-mcp probuilder info "MyCube" --include faces - unity-mcp probuilder info "-12345" --search-method by_id --include all + unity-mcp probuilder info --search-method by_id --include all -- -12345 """ config = get_config() request: dict[str, Any] = {"action": "get_mesh_info", "target": target, "include": include} @@ -493,7 +494,7 @@ def mesh_info(target: str, include: str, search_method: Optional[str]): def auto_smooth(target: str, angle: float, search_method: Optional[str]): """Auto-assign smoothing groups by angle threshold. - \\b + \b Examples: unity-mcp probuilder auto-smooth "MyCube" unity-mcp probuilder auto-smooth "MyCube" --angle 45 @@ -522,7 +523,7 @@ def auto_smooth(target: str, angle: float, search_method: Optional[str]): def set_smoothing(target: str, faces: str, group: int, search_method: Optional[str]): """Set smoothing group on specific faces. - \\b + \b Examples: unity-mcp probuilder set-smoothing "MyCube" --faces '[0,1,2]' --group 1 unity-mcp probuilder set-smoothing "MyCube" --faces '[3,4,5]' --group 0 @@ -556,7 +557,7 @@ def set_smoothing(target: str, faces: str, group: int, search_method: Optional[s def center_pivot(target: str, search_method: Optional[str]): """Move pivot point to mesh bounds center. - \\b + \b Examples: unity-mcp probuilder center-pivot "MyCube" """ @@ -579,7 +580,7 @@ def center_pivot(target: str, search_method: Optional[str]): def set_pivot(target: str, position, search_method: Optional[str]): """Set pivot to an arbitrary world position. - \\b + \b Examples: unity-mcp probuilder set-pivot "MyCube" --position 0 0 0 unity-mcp probuilder set-pivot "MyCube" --position 1.5 0 2.3 @@ -606,7 +607,7 @@ def set_pivot(target: str, position, search_method: Optional[str]): def freeze_transform(target: str, search_method: Optional[str]): """Bake position/rotation/scale into vertex data, reset transform. - \\b + \b Examples: unity-mcp probuilder freeze-transform "MyCube" """ @@ -628,7 +629,7 @@ def freeze_transform(target: str, search_method: Optional[str]): def validate_mesh(target: str, search_method: Optional[str]): """Check mesh health (degenerate triangles, unused vertices). - \\b + \b Examples: unity-mcp probuilder validate "MyCube" """ @@ -648,7 +649,7 @@ def validate_mesh(target: str, search_method: Optional[str]): def repair_mesh(target: str, search_method: Optional[str]): """Auto-fix degenerate triangles and unused vertices. - \\b + \b Examples: unity-mcp probuilder repair "MyCube" """ @@ -676,7 +677,7 @@ def repair_mesh(target: str, search_method: Optional[str]): def pb_raw(action: str, target: Optional[str], params: str, search_method: Optional[str]): """Execute any ProBuilder action directly. - \\b + \b Actions include: create_shape, create_poly_shape, extrude_faces, extrude_edges, bevel_edges, subdivide, @@ -691,7 +692,7 @@ def pb_raw(action: str, target: Optional[str], params: str, search_method: Optio set_smoothing, auto_smooth, center_pivot, set_pivot, freeze_transform, validate_mesh, repair_mesh - \\b + \b Examples: unity-mcp probuilder raw extrude_faces "MyCube" --params '{"faceIndices": [0], "distance": 1.0}' unity-mcp probuilder raw bevel_edges "MyCube" --params '{"edges": [{"a":0,"b":1}], "amount": 0.2}' diff --git a/Server/src/cli/commands/scene.py b/Server/src/cli/commands/scene.py index 34f32385c1..b46e6991e5 100644 --- a/Server/src/cli/commands/scene.py +++ b/Server/src/cli/commands/scene.py @@ -60,7 +60,7 @@ def hierarchy( unity-mcp scene hierarchy unity-mcp scene hierarchy --max-depth 3 unity-mcp scene hierarchy --parent "Canvas" --include-transform - unity-mcp scene hierarchy --format json + unity-mcp --format json scene hierarchy """ config = get_config() diff --git a/Server/src/cli/commands/script.py b/Server/src/cli/commands/script.py index d5caea79e9..a658bb4f87 100644 --- a/Server/src/cli/commands/script.py +++ b/Server/src/cli/commands/script.py @@ -12,6 +12,14 @@ from cli.utils.confirmation import confirm_destructive_action +def _name_and_folder(path: str) -> tuple[str, str]: + """Split 'Assets/Scripts/Player.cs' into ('Player', 'Assets/Scripts'), the form manage_script takes.""" + parts = path.replace("\\", "/").rsplit("/", 1) + filename = parts[-1] + directory = parts[0] if len(parts) > 1 else "Assets" + return (filename[:-3] if filename.endswith(".cs") else filename), directory + + @click.group() def script(): """Script operations - create, read, edit C# scripts.""" @@ -99,10 +107,7 @@ def read(path: str, start_line: Optional[int], line_count: Optional[int]): """ config = get_config() - parts = path.rsplit("/", 1) - filename = parts[-1] - directory = parts[0] if len(parts) > 1 else "Assets" - name = filename[:-3] if filename.endswith(".cs") else filename + name, directory = _name_and_folder(path) params: dict[str, Any] = { "action": "read", @@ -146,10 +151,7 @@ def delete(path: str, force: bool): confirm_destructive_action("Delete", "script", path, force) - parts = path.rsplit("/", 1) - filename = parts[-1] - directory = parts[0] if len(parts) > 1 else "Assets" - name = filename[:-3] if filename.endswith(".cs") else filename + name, directory = _name_and_folder(path) params: dict[str, Any] = { "action": "delete", @@ -181,15 +183,24 @@ def edit(path: str, edits: str): config = get_config() edits_list = parse_json_list_or_exit(edits, "edits") + name, directory = _name_and_folder(path) - params: dict[str, Any] = { - "uri": path, - "edits": edits_list, - } + # Unity refuses an edit that does not name the version of the file it changes. + sha_result = run_command("manage_script", {"action": "get_sha", "name": name, "path": directory}, config) + sha = (sha_result.get("result", sha_result).get("data") or {}).get("sha256") + if not sha: + click.echo(format_output(sha_result, config.format)) + sys.exit(1) - result = run_command("apply_text_edits", params, config) + result = run_command("manage_script", { + "action": "apply_text_edits", + "name": name, + "path": directory, + "edits": edits_list, + "precondition_sha256": sha, + }, config) click.echo(format_output(result, config.format)) - if result.get("success"): + if result.get("result", result).get("success"): print_success(f"Applied edits to: {path}") @@ -212,11 +223,11 @@ def validate(path: str, level: str): """ config = get_config() - params: dict[str, Any] = { - "uri": path, + name, directory = _name_and_folder(path) + result = run_command("manage_script", { + "action": "validate", + "name": name, + "path": directory, "level": level, - "include_diagnostics": True, - } - - result = run_command("validate_script", params, config) + }, config) click.echo(format_output(result, config.format)) diff --git a/Server/src/cli/commands/shader.py b/Server/src/cli/commands/shader.py index e5f08999a9..adf9829f3c 100644 --- a/Server/src/cli/commands/shader.py +++ b/Server/src/cli/commands/shader.py @@ -22,7 +22,7 @@ def shader(): def read_shader(path: str): """Read a shader file. - \\b + \b Examples: unity-mcp shader read "Assets/Shaders/MyShader.shader" """ @@ -69,7 +69,7 @@ def read_shader(path: str): def create_shader(name: str, path: str, contents: Optional[str], file_path: Optional[str]): """Create a new shader. - \\b + \b Examples: unity-mcp shader create "MyShader" --path "Assets/Shaders" unity-mcp shader create "MyShader" --file local_shader.shader @@ -155,7 +155,7 @@ def create_shader(name: str, path: str, contents: Optional[str], file_path: Opti def update_shader(path: str, contents: Optional[str], file_path: Optional[str]): """Update an existing shader. - \\b + \b Examples: unity-mcp shader update "Assets/Shaders/MyShader.shader" --file updated.shader echo "New shader code" | unity-mcp shader update "Assets/Shaders/MyShader.shader" @@ -203,7 +203,7 @@ def update_shader(path: str, contents: Optional[str], file_path: Optional[str]): def delete_shader(path: str, force: bool): """Delete a shader. - \\b + \b Examples: unity-mcp shader delete "Assets/Shaders/OldShader.shader" unity-mcp shader delete "Assets/Shaders/OldShader.shader" --force diff --git a/Server/src/cli/commands/texture.py b/Server/src/cli/commands/texture.py index 020154b229..8a2dd9db44 100644 --- a/Server/src/cli/commands/texture.py +++ b/Server/src/cli/commands/texture.py @@ -607,7 +607,7 @@ def modify(path: str, set_pixels: Optional[str], texture_type: Optional[str], sp def delete(path: str, force: bool): """Delete a texture. - \\b + \b Examples: unity-mcp texture delete "Assets/Textures/Old.png" unity-mcp texture delete "Assets/Textures/Old.png" --force diff --git a/Server/src/cli/commands/vfx.py b/Server/src/cli/commands/vfx.py index 0d0008ccc7..401b6e0bfc 100644 --- a/Server/src/cli/commands/vfx.py +++ b/Server/src/cli/commands/vfx.py @@ -57,10 +57,10 @@ def particle(): def particle_info(target: str, search_method: Optional[str], component_index: Optional[int]): """Get particle system info. - \\b + \b Examples: unity-mcp vfx particle info "Fire" - unity-mcp vfx particle info "-12345" --search-method by_id + unity-mcp vfx particle info --search-method by_id -- -12345 unity-mcp vfx particle info "Effects" --component-index 1 """ config = get_config() @@ -84,7 +84,7 @@ def particle_info(target: str, search_method: Optional[str], component_index: Op def particle_play(target: str, with_children: bool, search_method: Optional[str], component_index: Optional[int]): """Play a particle system. - \\b + \b Examples: unity-mcp vfx particle play "Fire" unity-mcp vfx particle play "Effects" --with-children @@ -210,7 +210,7 @@ def line(): def line_info(target: str, search_method: Optional[str], component_index: Optional[int]): """Get line renderer info. - \\b + \b Examples: unity-mcp vfx line info "LaserBeam" unity-mcp vfx line info "MultiLine" --component-index 1 @@ -236,7 +236,7 @@ def line_info(target: str, search_method: Optional[str], component_index: Option def line_set_positions(target: str, positions: str, search_method: Optional[str], component_index: Optional[int]): """Set all positions on a line renderer. - \\b + \b Examples: unity-mcp vfx line set-positions "Line" --positions "[[0,0,0], [5,2,0], [10,0,0]]" """ @@ -269,7 +269,7 @@ def line_set_positions(target: str, positions: str, search_method: Optional[str] def line_create_line(target: str, start: Tuple[float, float, float], end: Tuple[float, float, float], search_method: Optional[str], component_index: Optional[int]): """Create a simple line between two points. - \\b + \b Examples: unity-mcp vfx line create-line "MyLine" --start 0 0 0 --end 10 5 0 """ @@ -301,7 +301,7 @@ def line_create_line(target: str, start: Tuple[float, float, float], end: Tuple[ def line_create_circle(target: str, center: Tuple[float, float, float], radius: float, segments: int, search_method: Optional[str], component_index: Optional[int]): """Create a circle shape. - \\b + \b Examples: unity-mcp vfx line create-circle "Circle" --radius 5 --segments 64 unity-mcp vfx line create-circle "Ring" --center 0 2 0 --radius 3 @@ -381,7 +381,7 @@ def trail_info(target: str, search_method: Optional[str], component_index: Optio def trail_set_time(target: str, duration: float, search_method: Optional[str], component_index: Optional[int]): """Set trail duration. - \\b + \b Examples: unity-mcp vfx trail set-time "PlayerTrail" 2.0 """ @@ -436,14 +436,14 @@ def vfx_raw(action: str, target: Optional[str], params: str, search_method: Opti For advanced users who need access to all 60+ VFX actions. - \\b + \b Actions include: particle_*: particle_set_main, particle_set_emission, particle_set_shape, ... vfx_*: vfx_set_float, vfx_send_event, vfx_play, ... line_*: line_create_arc, line_create_bezier, ... trail_*: trail_set_width, trail_set_color, ... - \\b + \b Examples: unity-mcp vfx raw particle_set_main "Fire" --params '{"duration": 5, "looping": true}' unity-mcp vfx raw line_create_arc "Arc" --params '{"radius": 3, "startAngle": 0, "endAngle": 180}' diff --git a/Server/src/cli/main.py b/Server/src/cli/main.py index 216a5b86d8..6dad3d97a2 100644 --- a/Server/src/cli/main.py +++ b/Server/src/cli/main.py @@ -23,7 +23,6 @@ class Context: def __init__(self): self.config: Optional[CLIConfig] = None - self.verbose: bool = False pass_context = click.make_pass_decorator(Context, ensure=True) @@ -96,7 +95,7 @@ def _resolve_command_with_suggestions(self: click.Group, ctx: click.Context, arg @click.option( "--verbose", "-v", is_flag=True, - help="Enable verbose output." + help="Print each command sent to Unity and its raw response to stderr." ) @pass_context def cli(ctx: Context, host: str, port: int, timeout: int, format: str, instance: Optional[str], verbose: bool): @@ -108,7 +107,7 @@ def cli(ctx: Context, host: str, port: int, timeout: int, format: str, instance: Examples: unity-mcp status unity-mcp gameobject find "Player" - unity-mcp scene hierarchy --format json + unity-mcp --format json scene hierarchy unity-mcp editor play \b @@ -125,6 +124,7 @@ def cli(ctx: Context, host: str, port: int, timeout: int, format: str, instance: timeout=timeout, format=format, unity_instance=instance, + verbose=verbose, ) # Security warning for non-localhost connections @@ -132,7 +132,6 @@ def cli(ctx: Context, host: str, port: int, timeout: int, format: str, instance: set_config(config) ctx.config = config - ctx.verbose = verbose @cli.command("status") diff --git a/Server/src/cli/utils/config.py b/Server/src/cli/utils/config.py index 47299bb0dc..13a32882ec 100644 --- a/Server/src/cli/utils/config.py +++ b/Server/src/cli/utils/config.py @@ -14,6 +14,7 @@ class CLIConfig: timeout: int = 30 format: str = "text" # text, json, table unity_instance: Optional[str] = None + verbose: bool = False @classmethod def from_env(cls) -> "CLIConfig": diff --git a/Server/src/cli/utils/connection.py b/Server/src/cli/utils/connection.py index b2a0a42def..dfa5d3c46c 100644 --- a/Server/src/cli/utils/connection.py +++ b/Server/src/cli/utils/connection.py @@ -2,9 +2,11 @@ import asyncio import functools +import json import sys from typing import Any, Callable, Dict, Optional, TypeVar +import click import httpx from cli.utils.config import get_config, CLIConfig @@ -98,6 +100,9 @@ async def send_command( if cfg.unity_instance: payload["unity_instance"] = cfg.unity_instance + if cfg.verbose: + click.echo(f"> POST {url} {json.dumps(payload)}", err=True) + try: async with httpx.AsyncClient() as client: response = await client.post( @@ -105,6 +110,8 @@ async def send_command( json=payload, timeout=timeout or cfg.timeout, ) + if cfg.verbose: + click.echo(f"< {response.status_code} {response.text}", err=True) response.raise_for_status() return response.json() except httpx.ConnectError as e: diff --git a/Server/tests/test_cli.py b/Server/tests/test_cli.py index 65f64f781c..689c4d4a99 100644 --- a/Server/tests/test_cli.py +++ b/Server/tests/test_cli.py @@ -476,6 +476,13 @@ def test_camera_screenshot_scene_view(self, runner, mock_unity_response): assert params["viewTarget"] == "Canvas" assert params["includeImage"] is True + def test_camera_screenshot_position_view_target_reaches_unity_as_an_array(self, runner, mock_unity_response): + # Unity reads a JSON array as a position; the text "[0, 1, 2]" would be looked up as a GameObject name. + with patch("cli.commands.camera.run_command", return_value=mock_unity_response) as mock_run: + result = runner.invoke(cli, ["camera", "screenshot", "--view-target", "[0, 1, 2]"]) + assert result.exit_code == 0, result.output + assert mock_run.call_args[0][1]["viewTarget"] == [0, 1, 2] + def test_camera_ping_prints_output(self, runner, mock_unity_response): """The camera group must actually emit its result. @@ -918,6 +925,38 @@ def test_material_assign(self, runner, mock_unity_response): class TestScriptCommands: """Tests for Script CLI commands.""" + # validate_script and apply_text_edits are server-side MCP tools: Unity has no + # command by either name, so the CLI must call manage_script's actions itself. + def test_script_validate_calls_manage_script_validate(self, runner, mock_unity_response): + # A Windows path splits into the same name and folder as a forward-slash one. + with patch("cli.commands.script.run_command", return_value=mock_unity_response) as mock_run: + result = runner.invoke(cli, ["script", "validate", "Assets\\Scripts\\Player.cs", "--level", "standard"]) + assert result.exit_code == 0, result.output + assert mock_run.call_args[0][:2] == ("manage_script", { + "action": "validate", "name": "Player", "path": "Assets/Scripts", "level": "standard"}) + + def test_script_edit_reads_the_sha_then_applies_the_edits_with_it(self, runner): + # Unity refuses an edit that does not name the version of the file it changes. + edits = [{"startLine": 1, "startCol": 1, "endLine": 1, "endCol": 1, "newText": "// x"}] + replies = [{"status": "success", "result": {"success": True, "data": {"sha256": "abc123"}}}, + {"status": "success", "result": {"success": True}}] + with patch("cli.commands.script.run_command", side_effect=replies) as mock_run: + result = runner.invoke(cli, ["script", "edit", "Assets/Scripts/Player.cs", "--edits", json.dumps(edits)]) + assert result.exit_code == 0, result.output + assert [c[0][1] for c in mock_run.call_args_list] == [ + {"action": "get_sha", "name": "Player", "path": "Assets/Scripts"}, + {"action": "apply_text_edits", "name": "Player", "path": "Assets/Scripts", + "edits": edits, "precondition_sha256": "abc123"}, + ] + + def test_script_edit_exits_with_an_error_when_the_sha_lookup_fails(self, runner): + missing = {"status": "error", "error": "Script 'Player.cs' not found."} + with patch("cli.commands.script.run_command", return_value=missing) as mock_run: + result = runner.invoke(cli, ["script", "edit", "Assets/Scripts/Player.cs", "--edits", "[]"]) + assert result.exit_code == 1, result.output + assert "not found" in result.output + assert mock_run.call_count == 1 + def test_script_create(self, runner, mock_unity_response): """Test script create command.""" with patch("cli.commands.script.run_command", return_value=mock_unity_response): @@ -997,6 +1036,21 @@ def test_timeout_option(self, runner, mock_unity_response): result = runner.invoke(cli, ["--timeout", "60", "status"]) assert result.exit_code == 0 + def test_verbose_prints_request_and_response(self, runner, mock_unity_response): + """Test -v prints the command sent to Unity and the raw response to stderr.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.text = json.dumps(mock_unity_response) + mock_response.json.return_value = mock_unity_response + with patch("httpx.AsyncClient") as mock_client: + mock_client.return_value.__aenter__.return_value.post = AsyncMock( + return_value=mock_response + ) + result = runner.invoke(cli, ["-v", "scene", "active"]) + assert result.exit_code == 0 + assert '{"type": "manage_scene", "params": {"action": "get_active"}}' in result.stderr + assert '"message": "Operation successful"' in result.stderr + # ============================================================================= # Error Handling Tests @@ -1126,13 +1180,6 @@ def test_instance_list(self, runner): assert result.exit_code == 0 assert "TestProject" in result.output - def test_instance_set(self, runner, mock_unity_response): - """Test setting active instance.""" - with patch("cli.commands.instance.run_command", return_value=mock_unity_response): - result = runner.invoke( - cli, ["instance", "set", "TestProject@abc123"]) - assert result.exit_code == 0 - def test_instance_current(self, runner): """Test showing current instance.""" result = runner.invoke(cli, ["instance", "current"]) diff --git a/Server/tests/test_skill_copies_in_sync.py b/Server/tests/test_skill_copies_in_sync.py new file mode 100644 index 0000000000..83cb0c5cb4 --- /dev/null +++ b/Server/tests/test_skill_copies_in_sync.py @@ -0,0 +1,33 @@ +"""The agent skill lives in two directories that must hold the same files. + +`.claude/skills/unity-mcp-skill/` is the copy the Unity editor installs: its Install Skills +button mirrors that subtree from GitHub (SkillSyncService.SkillSubdir). `unity-mcp-skill/` at +the repo root is the copy users download by hand. Edits landed in one copy only, until the +installed skill linked two reference files it did not ship. +""" +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] +PUBLIC = REPO_ROOT / "unity-mcp-skill" +INSTALLED = REPO_ROOT / ".claude" / "skills" / "unity-mcp-skill" + + +def _files(root: Path) -> dict[str, bytes]: + # Line endings are normalised: core.autocrlf decides them per checkout, not per edit. + return { + path.relative_to(root).as_posix(): path.read_bytes().replace(b"\r\n", b"\n") + for path in root.rglob("*") + if path.is_file() + } + + +def test_skill_copies_are_identical(): + public, installed = _files(PUBLIC), _files(INSTALLED) + assert public, f"no skill files under {PUBLIC}" + + problems = [f"only in unity-mcp-skill/: {name}" for name in sorted(public.keys() - installed.keys())] + problems += [f"only in .claude/skills/unity-mcp-skill/: {name}" + for name in sorted(installed.keys() - public.keys())] + problems += [f"differs between the copies: {name}" + for name in sorted(public.keys() & installed.keys()) if public[name] != installed[name]] + assert not problems, "\n".join(problems) diff --git a/docs/i18n/README-zh.md b/docs/i18n/README-zh.md index ac57ff9fe9..5bf82ab963 100644 --- a/docs/i18n/README-zh.md +++ b/docs/i18n/README-zh.md @@ -25,11 +25,11 @@
最近更新 -* **[v10.0.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.0.0)**(2026-06-30) -* **[v9.7.3](https://github.com/CoplayDev/unity-mcp/releases/tag/v9.7.3)**(2026-06-15) -* **[v9.7.1](https://github.com/CoplayDev/unity-mcp/releases/tag/v9.7.1)**(2026-05-24) -* **[v9.7.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v9.7.0)**(2026-05-22) -* **[v9.6.8](https://github.com/CoplayDev/unity-mcp/releases/tag/v9.6.8)**(2026-04-27) +* **[v10.3.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.3.0)**(2026-10-04) +* **[v10.2.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.2.0)**(2026-09-01) +* **[v10.1.2](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.1.2)**(2026-08-02) +* **[v10.1.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.1.0)**(2026-07-13) +* **[v10.0.2](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.0.2)**(2026-07-13) 完整更新历史见 [发布说明](https://coplaydev.github.io/unity-mcp/releases)。 diff --git a/tools/UPDATE_DOCS_PROMPT.md b/tools/UPDATE_DOCS_PROMPT.md index c769a4d580..50e54359e6 100644 --- a/tools/UPDATE_DOCS_PROMPT.md +++ b/tools/UPDATE_DOCS_PROMPT.md @@ -1,81 +1,39 @@ # LLM Prompt for Updating Documentation -Copy and paste this prompt into your LLM when you need to update documentation after adding/removing/modifying MCP tools or resources. - -## Example Usage - -After adding a new tool called "manage_new_feature" and a new resource called "feature_resource", you would: -1. Copy the prompt in the section below -2. Paste it into your LLM -3. The LLM will analyze the codebase and update all documentation files -4. Review the changes and run the check script to verify - -This ensures all documentation stays in sync across the repository. +Copy and paste this prompt into your LLM after you add, remove, rename or change an MCP tool or resource. Review its changes, then make sure the checks in step 5 pass. --- ## Prompt -I've just made changes to MCP tools or resources in this Unity MCP repository. Please update all documentation files to keep them in sync. - -Here's what you need to do: +I've just changed MCP tools or resources in this MCP for Unity repository. Please update the documentation to match. -1. **Check the current tools and resources** by examining: - - `Server/src/services/tools/` - Python tool implementations (look for @mcp_for_unity_tool decorators) - - `Server/src/services/resources/` - Python resource implementations (look for @mcp_for_unity_resource decorators) +1. **Read the current tools and resources**: + - `Server/src/services/tools/`: tools (`@mcp_for_unity_tool`, including its `group`) + - `Server/src/services/resources/`: resources (`@mcp_for_unity_resource`) + - `Server/src/cli/commands/`: the Click CLI commands that mirror the tools -2. **Update these files**: - - a) **manifest.json** (root directory) - - Update the "tools" array (lines 27-57) - - Each tool needs: {"name": "tool_name", "description": "Brief description"} - - Keep tools in alphabetical order - - Note: Resources are not listed in manifest.json, only tools - - b) **README.md** (root directory) - - Update "Available Tools" section (around line 78-79) - - Format: `tool1` • `tool2` • `tool3` - - Keep the same order as manifest.json - - c) **README.md** - Resources section - - Update "Available Resources" section (around line 81-82) - - Format: `resource1` • `resource2` • `resource3` - - Resources come from Server/src/services/resources/ files - - Keep resources in alphabetical order - - d) **docs/i18n/README-zh.md** - - Find and update the "可用工具" (Available Tools) section - - Find and update the "可用资源" (Available Resources) section - - Keep tool/resource names in English, but you can translate descriptions if helpful - - e) **README.md** — "Recent Updates" section - - Add a new entry at the top of the list for the current version - - Format: `* **vX.Y.Z (beta)** — Brief summary of what changed` - - Keep only 4 entries visible; move the oldest to the "Older releases" nested details block - - Remove `(beta)` from the previous entry that was beta - - Update `manifest.json` version field to match - - f) **docs/i18n/README-zh.md** — "最近更新" section - - Mirror the same changes as the English "Recent Updates" section - - Translate the summary text to Chinese - - Same 4-entry rotation rule applies +2. **Regenerate the reference pages.** `website/docs/reference/tools/**` and `website/docs/reference/resources/index.md` are generated from the registry: + ```bash + cd Server && uv run python ../tools/generate_docs_reference.py + ``` + Do not hand-edit them, except for usage examples between `` and ``, which the generator keeps. - g) **unity-mcp-skill** - Skill Update - - Detect if this feature needs extra care via Skills - - If so, update the .md files based on the updates +3. **Update the hand-maintained files**: + - **manifest.json**: the `tools` array must list exactly the registered tools as `{"name", "description"}` entries; put new ones in alphabetical position. Resources are not listed. `Server/tests/test_manifest_tools.py` fails when a tool is missing, extra or listed twice, but it compares names only: when a tool's purpose changes, check its one-line description by hand. + - **Tool groups**: if a tool adds a group or changes what a group covers, update the group's blurb in `TOOL_GROUPS` (`Server/src/services/registry/tool_registry.py`; it feeds the generated group pages, the `tool_groups` resource and `manage_tools`) and the group lists in `website/docs/guides/tool-groups.md` and `website/docs/contributing/dev-setup.md`. + - **Tool count**: `README.md`, `docs/i18n/README-zh.md` and `website/docs/guides/tool-groups.md` state how many tools ship. Update the number if it changed. + - **CLI docs**: the group table in `website/docs/reference/cli.md` and the "Complete Command Reference" table in `website/docs/guides/cli.md` must match the Click command tree (`cd Server && uv run unity-mcp --help`, then `--help` on each group). + - **Skill**: if agents need new guidance for the change, update the skill. It has two copies, `unity-mcp-skill/` and `.claude/skills/unity-mcp-skill/`; keep both copies identical. -3. **Important formatting rules**: - - Use backticks around tool/resource names - - Separate items with • (bullet point) - - Keep lists on single lines when possible - - Maintain alphabetical ordering - - Tools and resources are listed separately in documentation +4. **Leave the release notes alone.** `README.md` has no tool or resource lists; it links to the website catalog. Its "Recent Updates" block (the latest 5 releases) and `website/docs/releases.md` are generated from GitHub Releases, whose notes GitHub builds from merged PR titles. After each release, `release.yml` dispatches `.github/workflows/sync-releases.yml`, which runs `tools/sync_release_notes.py` and merges the result into `beta` through its own PR. The "最近更新" block in `docs/i18n/README-zh.md` is not synced; it is updated by hand at release time. -4. **After updating**, run this check to verify: +5. **After updating**, run these checks: ```bash - python3 tools/check_docs_sync.py + cd Server + uv run python ../tools/generate_docs_reference.py --check + uv run pytest tests/test_manifest_tools.py ``` - It should show "All documentation is synchronized!" Please show me the exact changes you're making to each file, and explain any discrepancies you find. diff --git a/tools/generate_docs_reference.py b/tools/generate_docs_reference.py index dc94dc9a08..0d8e77a2ad 100644 --- a/tools/generate_docs_reference.py +++ b/tools/generate_docs_reference.py @@ -439,16 +439,18 @@ def render_resources_catalog(resources: list[dict[str, Any]]) -> str: # --------------------------------------------------------------------------- +# Same text as TOOL_GROUPS in Server/src/services/registry/tool_registry.py, so a fallback run writes the same pages. GROUP_BLURBS_FALLBACK = { - "core": "Essential scene, script, asset, and editor tools — always on by default.", - "docs": "Unity API reflection and documentation lookup.", - "vfx": "Visual effects — VFX Graph, shaders, procedural textures.", - "animation": "Animator control and AnimationClip creation.", - "ui": "UI Toolkit — UXML, USS, UIDocument.", - "scripting_ext": "ScriptableObject management.", - "testing": "Test runner and async test jobs.", - "probuilder": "ProBuilder 3D modeling — requires `com.unity.probuilder`.", - "profiling": "Unity Profiler session control, counters, memory snapshots, Frame Debugger.", + "core": "Essential scene, script, asset & editor tools (always on by default)", + "docs": "Unity API reflection and documentation lookup", + "vfx": "Visual effects – VFX Graph, shaders, procedural textures", + "animation": "Animator control, AnimationClip creation & 2D sprite-sheet animation", + "ui": "UI Toolkit (UXML, USS, UIDocument)", + "scripting_ext": "ScriptableObject management", + "testing": "Test runner & async test jobs", + "probuilder": "ProBuilder 3D modeling – requires com.unity.probuilder package", + "profiling": "Unity Profiler session control, counters, memory snapshots & Frame Debugger", + "asset_gen": "AI asset generation – 3D model gen/import, 2D image gen & audio gen (bring-your-own-key)", } diff --git a/tools/sync_release_notes.py b/tools/sync_release_notes.py index 1d0d6177be..df279120c2 100644 --- a/tools/sync_release_notes.py +++ b/tools/sync_release_notes.py @@ -89,19 +89,18 @@ def _fetch_via_gh(path: str) -> list[dict] | None: except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: print(f"gh api failed ({e}); falling back to urllib.", file=sys.stderr) return None - # `gh api --paginate` concatenates JSON arrays as `][`. Split + parse. + # `gh api --paginate` prints one JSON array per page, back to back. Decode them in + # turn: replacing every `][` with `,` also rewrote release bodies that contain `][`. text = result.stdout.strip() - if not text: - return [] - if "][" in text: - text = "[" + text.replace("][", ",") + "]" - # Now we may have [[..],[..]] — flatten. - nested = json.loads(text) - flat: list[dict] = [] - for chunk in nested: - flat.extend(chunk if isinstance(chunk, list) else [chunk]) - return flat - return json.loads(text) + decoder = json.JSONDecoder() + releases: list[dict] = [] + pos = 0 + while pos < len(text): + page, pos = decoder.raw_decode(text, pos) + releases.extend(page) + while pos < len(text) and text[pos].isspace(): + pos += 1 + return releases def _fetch_via_urllib(url: str) -> list[dict]: diff --git a/tools/tests/test_release_workflow.py b/tools/tests/test_release_workflow.py new file mode 100644 index 0000000000..2ef5ae2391 --- /dev/null +++ b/tools/tests/test_release_workflow.py @@ -0,0 +1,31 @@ +"""release.yml must start the release-notes sync itself. + +It creates the release with GITHUB_TOKEN, and events made with that token start no workflows +(only workflow_dispatch and repository_dispatch are exempt), so sync-releases.yml never saw +`release: published` and README/releases.md stopped at v10.0.0. +""" +from pathlib import Path +import re + + +WORKFLOWS = Path(__file__).resolve().parents[2] / ".github" / "workflows" + + +def release_jobs(): + text = (WORKFLOWS / "release.yml").read_text(encoding="utf-8") + body = text.split("\njobs:\n", 1)[1] + starts = [match.start() for match in re.finditer(r"^ \w+:$", body, re.M)] + [len(body)] + return [body[begin:end] for begin, end in zip(starts, starts[1:])] + + +def test_release_dispatches_the_notes_sync_which_lands_on_beta_through_a_pr(): + jobs = [job for job in release_jobs() if "gh workflow run sync-releases.yml" in job] + assert len(jobs) == 1, "release.yml must dispatch sync-releases.yml" + assert re.search(r"gh workflow run sync-releases\.yml\b.*--ref beta\b", jobs[0]), jobs[0] + assert re.search(r"^ actions: write$", jobs[0], re.M), jobs[0] + + sync = (WORKFLOWS / "sync-releases.yml").read_text(encoding="utf-8") + assert re.search(r"^ workflow_dispatch:", sync, re.M), sync + # The beta ruleset requires a pull request, so a direct push is rejected. + assert "gh pr create" in sync and "--base beta" in sync, sync + assert not re.search(r"git push\b.*\bbeta\b", sync), sync diff --git a/tools/tests/test_sync_release_notes.py b/tools/tests/test_sync_release_notes.py new file mode 100644 index 0000000000..608138f2af --- /dev/null +++ b/tools/tests/test_sync_release_notes.py @@ -0,0 +1,23 @@ +"""`gh api --paginate` prints one JSON array per page; joining them must not touch release bodies.""" +import json +import sys +from pathlib import Path +from types import SimpleNamespace + +_TOOLS_DIR = Path(__file__).resolve().parents[1] +if str(_TOOLS_DIR) not in sys.path: + sys.path.insert(0, str(_TOOLS_DIR)) + +import sync_release_notes # noqa: E402 + + +def test_pages_are_joined_without_rewriting_release_bodies(monkeypatch): + first = [{"tag_name": "v2", "body": "See [the guide][1] and [notes][2]."}] + second = [{"tag_name": "v1", "body": "plain"}] + monkeypatch.setattr(sync_release_notes.shutil, "which", lambda _: "gh") + monkeypatch.setattr( + sync_release_notes.subprocess, "run", + lambda *a, **k: SimpleNamespace(stdout=json.dumps(first) + json.dumps(second)), + ) + + assert sync_release_notes._fetch_via_gh("repos/o/r/releases") == first + second diff --git a/unity-mcp-skill/SKILL.md b/unity-mcp-skill/SKILL.md index 743d47feb5..8e3467b873 100644 --- a/unity-mcp-skill/SKILL.md +++ b/unity-mcp-skill/SKILL.md @@ -183,11 +183,11 @@ uri="file:///full/path/to/file.cs" | Category | Key Tools | Use For | |----------|-----------|---------| -| **Scene** | `manage_scene`, `find_gameobjects` | Scene operations, finding objects | +| **Scene** | `manage_scene`, `find_gameobjects` | Scene operations, finding objects. Multi-scene editing (additive load, close, set active, move GO between scenes), scene templates (`3d_basic`, `2d_basic`, `empty`, `default`), scene validation with `auto_repair`. For build settings, use `manage_build(action="scenes")`. | | **Objects** | `manage_gameobject`, `manage_components` | Creating/modifying GameObjects | | **Scripts** | `create_script`, `script_apply_edits`, `validate_script` | C# code management (auto-refreshes on create/edit) | | **Assets** | `manage_asset`, `manage_prefabs` | Asset operations. **Prefab instantiation** is done via `manage_gameobject(action="create", prefab_path="...")`, not `manage_prefabs`. | -| **Editor** | `manage_editor`, `execute_menu_item`, `read_console` | Editor control, package deployment (`deploy_package`/`restore_package` actions) | +| **Editor** | `manage_editor`, `execute_menu_item`, `read_console` | Editor control, package deployment (`deploy_package`/`restore_package`), undo/redo (`undo`/`redo` actions) | | **Testing** | `run_tests`, `get_test_job` | Unity Test Framework | | **Batch** | `batch_execute` | Parallel/bulk operations | | **Camera** | `manage_camera` | Camera management (Unity Camera + Cinemachine). **Tier 1** (always available): create, target, lens, priority, list, screenshot. **Tier 2** (requires `com.unity.cinemachine`): brain, body/aim/noise pipeline, extensions, blending, force/release. 7 presets: follow, third_person, freelook, dolly, static, top_down, side_scroller. Resource: `mcpforunity://scene/cameras`. Use `ping` to check Cinemachine availability. See [tools-reference.md](references/tools-reference.md#camera-tools). | @@ -198,6 +198,7 @@ uri="file:///full/path/to/file.cs" | **ProBuilder** | `manage_probuilder` | 3D modeling, mesh editing, complex geometry. **When `com.unity.probuilder` is installed, prefer ProBuilder shapes over primitive GameObjects** for editable geometry, multi-material faces, or complex shapes. Supports 12 shape types, face/edge/vertex editing, smoothing, and per-face materials. See [ProBuilder Guide](references/probuilder-guide.md). | | **UI** | `manage_ui`, `batch_execute` with `manage_gameobject` + `manage_components` | **UI Toolkit**: Use `manage_ui` to create UXML/USS files, attach UIDocument, inspect visual trees. **uGUI (Canvas)**: Use `batch_execute` for Canvas, Panel, Button, Text, Slider, Toggle, Input Field. **Read `mcpforunity://project/info` first** to detect uGUI/TMP/Input System/UI Toolkit availability. (see [UI workflows](references/workflows.md#ui-creation-workflows)) | | **Docs** | `unity_reflect`, `unity_docs` | API verification and documentation lookup. **`unity_reflect`** inspects live C# APIs via reflection (requires Unity connection): `search` types across assemblies, `get_type` for member summary, `get_member` for full signatures. **`unity_docs`** fetches official docs from docs.unity3d.com (no Unity connection needed): `get_doc` (ScriptReference), `get_manual` (Manual pages), `get_package_doc` (package docs), `lookup` (parallel search all sources + project assets). **Trust hierarchy: reflection > project assets > docs.** Workflow: `unity_reflect` search -> get_type -> get_member -> `unity_docs` lookup. See [tools-reference.md](references/tools-reference.md#docs-tools). | +| **More** | `manage_material`, `manage_texture`, `manage_shader`, `manage_vfx`, `manage_profiler`, `execute_code`, `manage_scriptable_object`, `generate_image`, `generate_model`, `generate_audio`, `import_model`, `import_model_file`, `blender_bridge` | Materials; textures, shaders and VFX (`vfx` group); profiling (`profiling`); in-editor C# and ScriptableObjects (`scripting_ext`); AI asset generation, Sketchfab and on-disk model import, Blender (`asset_gen`). Every tool has a section in [tools-reference.md](references/tools-reference.md). | ## Common Workflows @@ -283,6 +284,7 @@ set_active_instance(instance="MyProject@abc123") For detailed schemas and examples: -- **[tools-reference.md](references/tools-reference.md)**: Complete tool documentation with all parameters +- **[tools-reference.md](references/tools-reference.md)**: Every tool's main actions, parameters, and examples, with links to the full parameter tables - **[resources-reference.md](references/resources-reference.md)**: All available resources and their data - **[workflows.md](references/workflows.md)**: Extended workflow examples and patterns +- **[probuilder-guide.md](references/probuilder-guide.md)**: ProBuilder modeling patterns diff --git a/unity-mcp-skill/references/probuilder-guide.md b/unity-mcp-skill/references/probuilder-guide.md index a65ad841d9..d358d832e3 100644 --- a/unity-mcp-skill/references/probuilder-guide.md +++ b/unity-mcp-skill/references/probuilder-guide.md @@ -177,7 +177,8 @@ manage_probuilder(action="create_polygon", target="MyMesh", ## Vertex Operations ```python -# Move vertices by offset +# Move vertices by offset. Only the listed indices move: faces that meet at a corner +# usually each have their own vertex there, so moving one face's corners opens a gap. manage_probuilder(action="move_vertices", target="MyCube", properties={"vertexIndices": [0, 1, 2, 3], "offset": [0, 1, 0]}) @@ -260,13 +261,14 @@ info = manage_probuilder(action="get_mesh_info", target="House", manage_probuilder(action="extrude_faces", target="House", properties={"faceIndices": [2], "distance": 0.3}) -# 4. Re-query faces, then move top vertices inward to form a ridge -info = manage_probuilder(action="get_mesh_info", target="House", - properties={"include": "faces"}) -# Find the new top face after extrude, get its vertex indices -# Move them to form a peaked roof shape -manage_probuilder(action="move_vertices", target="House", - properties={"vertexIndices": [0, 1, 2, 3], "offset": [0, 2, 0]}) +# 4. Add a peaked roof as a separate Prism; its ridge runs along Z, like the house. +# (Moving the top face's vertices would pull it away from the walls; see Vertex Operations.) +# Shapes are centered on their position: the walls now reach y = 1.8, +# so a roof 2 high sits on them at y = 1.8 + 1. +manage_probuilder(action="create_shape", properties={ + "shape_type": "Prism", "name": "Roof", + "width": 6, "height": 2, "depth": 8, "position": [0, 2.8, 0] +}) # 5. Cut a doorway: subdivide front face, delete center sub-face info = manage_probuilder(action="get_mesh_info", target="House", @@ -300,8 +302,9 @@ manage_probuilder(action="auto_smooth", target="House", # 9. Assign materials per face manage_probuilder(action="set_face_material", target="House", properties={"faceIndices": [0, 1, 2, 3], "materialPath": "Assets/Materials/Brick.mat"}) -manage_probuilder(action="set_face_material", target="House", - properties={"faceIndices": [4, 5], "materialPath": "Assets/Materials/Roof.mat"}) +# A Prism's faces 1 and 3 are its two slopes +manage_probuilder(action="set_face_material", target="Roof", + properties={"faceIndices": [1, 3], "materialPath": "Assets/Materials/Roof.mat"}) # 10. Cleanup manage_probuilder(action="center_pivot", target="House") diff --git a/unity-mcp-skill/references/resources-reference.md b/unity-mcp-skill/references/resources-reference.md index a2aa74e3e6..ddd4fda90c 100644 --- a/unity-mcp-skill/references/resources-reference.md +++ b/unity-mcp-skill/references/resources-reference.md @@ -23,7 +23,7 @@ All resources use `mcpforunity://` scheme: mcpforunity://{category}/{resource_path}[?query_params] ``` -**Categories:** `editor`, `scene`, `prefab`, `project`, `pipeline`, `rendering`, `menu-items`, `custom-tools`, `tests`, `instances` +**Categories:** `editor`, `scene`, `prefab`, `project`, `pipeline`, `rendering`, `menu-items`, `custom-tools`, `tool-groups`, `tests`, `instances` --- @@ -506,7 +506,7 @@ Assets/Prefabs/Player.prefab → Assets%2FPrefabs%2FPlayer.prefab **Purpose:** Custom tools available in the active Unity project. -**Returns:** +**Returns** (abbreviated: one of the 3 tools is shown): ```json { "project_id": "MyProject", @@ -525,6 +525,23 @@ Assets/Prefabs/Player.prefab → Assets%2FPrefabs%2FPlayer.prefab } ``` +### mcpforunity://tool-groups + +**Purpose:** Every tool group, whether it is on by default, and its tools. Enable or hide a group for the session with `manage_tools`. + +**Returns** (abbreviated: `groups` has one entry per group, in name order; one is shown): +```json +{ + "groups": [ + {"name": "animation", "description": "Animator control, AnimationClip creation & 2D sprite-sheet animation", + "default_enabled": false, "tools": ["manage_animation", "manage_sprite"], "tool_count": 2} + ], + "total_groups": 10, + "default_enabled": ["core"], + "usage": "Call manage_tools(action='activate', group='') to enable a group." +} +``` + --- ## Instance Resources diff --git a/unity-mcp-skill/references/tools-reference.md b/unity-mcp-skill/references/tools-reference.md index 9bf1d41e51..2ff79a1186 100644 --- a/unity-mcp-skill/references/tools-reference.md +++ b/unity-mcp-skill/references/tools-reference.md @@ -1,6 +1,8 @@ # Unity-MCP Tools Reference -Complete reference for all MCP tools. Each tool includes parameters, types, and usage examples. +Every built-in MCP tool, with its main actions, parameters, and usage examples. For a tool's full parameter table, open its generated page: `https://coplaydev.github.io/unity-mcp/reference/tools//` (tools with no group, such as `manage_tools`, are under `core`). + +Each section names the tool's group when it is not `core`. A tool outside `core` can be hidden (over HTTP, only `core` starts enabled); enable its group with `manage_tools(action="activate", group="")`. > **Template warning:** Examples in this file are skill templates and may be inaccurate for some Unity versions, packages, or project setups. Validate parameters and payload shapes against your active tool schema and runtime behavior. @@ -14,7 +16,10 @@ Complete reference for all MCP tools. Each tool includes parameters, types, and - [Material & Shader Tools](#material--shader-tools) - [UI Tools](#ui-tools) - [Editor Control Tools](#editor-control-tools) +- [Build Tools](#build-tools) - [Testing Tools](#testing-tools) +- [Search Tools](#search-tools) +- [Custom Tools](#custom-tools) - [Camera Tools](#camera-tools) - [Graphics Tools](#graphics-tools) - [Package Tools](#package-tools) @@ -22,6 +27,10 @@ Complete reference for all MCP tools. Each tool includes parameters, types, and - [ProBuilder Tools](#probuilder-tools) - [Profiler Tools](#profiler-tools) - [Docs Tools](#docs-tools) +- [Animation Tools](#animation-tools) +- [VFX Tools](#vfx-tools) +- [Scripting Extension Tools](#scripting-extension-tools) +- [Asset Generation Tools](#asset-generation-tools) --- @@ -98,13 +107,36 @@ refresh_unity( ) ``` +### manage_tools + +Show, enable, or hide tool groups for the current session. Always visible (no group). + +```python +manage_tools(action="list_groups") # every group, whether it is on, and its tools +manage_tools(action="activate", group="animation") # "deactivate" hides a group again +manage_tools(action="sync") # re-read the tool toggles in the Unity editor +manage_tools(action="reset") # back to the defaults +``` + +Groups: `core`, `animation`, `asset_gen`, `docs`, `probuilder`, `profiling`, `scripting_ext`, `testing`, `ui`, `vfx`. The `mcpforunity://tool-groups` resource lists the same catalog read-only. [Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/manage_tools) + +### debug_request_context + +Diagnostics: returns the request's `client_id`, `session_id` and meta, the session's active Unity instance, and the server version. No parameters; always visible (no group). + +```python +debug_request_context() +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/debug_request_context) + --- ## Scene Tools ### manage_scene -Scene CRUD operations, hierarchy queries, screenshots, and scene view control. +Scene CRUD operations, hierarchy queries, multi-scene editing, validation, and scene view control. Screenshots are `manage_camera` actions (see [Camera Tools](#camera-tools)); build-settings scenes are `manage_build(action="scenes")`. ```python # Get hierarchy (paginated) @@ -116,55 +148,6 @@ manage_scene( include_transform=False # bool - include local transforms ) -# Screenshot (file only — saves to Assets/Screenshots/) -manage_camera(action="screenshot") - -# Screenshot with inline image (base64 PNG returned to AI) -manage_scene( - action="screenshot", - camera="MainCamera", # str, optional - camera name, path, or instance ID - include_image=True, # bool, default False - return base64 PNG inline - max_resolution=512 # int, optional - downscale cap (default 640) -) - -# Batch surround — contact sheet of 6 fixed angles (front/back/left/right/top/bird_eye) -manage_scene( - action="screenshot", - batch="surround", # str - "surround" for 6-angle contact sheet - max_resolution=256 # int - per-tile resolution cap -) -# Returns: single composite contact sheet image with labeled tiles - -# Batch surround centered on a specific target -manage_scene( - action="screenshot", - batch="surround", - view_target="Player", # str|int|list[float] - center surround on this target - max_resolution=256 -) - -# Batch orbit — configurable multi-angle grid around a target -manage_scene( - action="screenshot", - batch="orbit", # str - "orbit" for configurable angle grid - view_target="Player", # str|int|list[float] - target to orbit around - orbit_angles=8, # int, default 8 - number of azimuth steps - orbit_elevations=[0, 30], # list[float], default [0, 30, -15] - vertical angles in degrees - orbit_distance=10, # float, optional - camera distance (auto-fit if omitted) - orbit_fov=60, # float, default 60 - camera FOV in degrees - max_resolution=256 # int - per-tile resolution cap -) -# Returns: single composite contact sheet (angles × elevations tiles in a grid) - -# Positioned screenshot (temp camera at viewpoint, no file saved) -manage_scene( - action="screenshot", - view_target="Enemy", # str|int|list[float] - target to aim at - view_position=[0, 10, -10], # list[float], optional - camera position - view_rotation=[45, 0, 0], # list[float], optional - euler angles (overrides view_target aim) - max_resolution=512 -) - # Frame scene view on target manage_scene( action="scene_view_frame", @@ -177,6 +160,20 @@ 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") + +# Scene templates: "empty" (no objects), "default", "3d_basic", "2d_basic" +manage_scene(action="create", name="Level1", path="Assets/Scenes/", template="3d_basic") + +# Multi-scene editing +manage_scene(action="load", path="Assets/Scenes/Level2.unity", additive=True) # keep current scene +manage_scene(action="get_loaded_scenes") +manage_scene(action="set_active_scene", scene_name="Level2") +manage_scene(action="move_to_scene", target="Player", scene_name="Level2") # moves a root GameObject +manage_scene(action="close_scene", scene_name="Level2", remove_scene=True) # remove_scene=False only unloads + +# Validation: missing scripts, broken prefabs +manage_scene(action="validate") +manage_scene(action="validate", auto_repair=True) # also removes missing scripts (undoable) ``` ### find_gameobjects @@ -334,6 +331,8 @@ manage_components( # - "Assets/Prefabs/My.prefab" → String shorthand for asset paths # - "ObjectName" → String shorthand for scene name lookup # - 12345 → Integer shorthand for instanceID +# - {"guid": "...", "spriteName": "Hero_0"} or {"guid": "...", "fileID": 12345} +# → Sprite sub-asset of a sliced sheet (single-sprite textures resolve from guid/path alone) ``` --- @@ -444,7 +443,7 @@ Get file hash without content (for preconditions). ```python get_sha(uri="mcpforunity://path/Assets/Scripts/MyScript.cs") -# Returns: {"sha256": "...", "lengthBytes": 1234, "lastModifiedUtc": "..."} +# Returns: {"sha256": "...", "lengthBytes": 1234} ``` ### delete_script @@ -455,6 +454,26 @@ Delete a script file. delete_script(uri="mcpforunity://path/Assets/Scripts/OldScript.cs") ``` +### manage_script + +Legacy whole-file router with three actions: `create`, `read`, `delete`. `name` is the class name without `.cs`; `path` is its folder under `Assets/`. There is no update action: edit with `script_apply_edits` or `apply_text_edits`. + +```python +manage_script(action="read", name="PlayerController", path="Assets/Scripts") # returns the file contents +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/manage_script) + +### manage_script_capabilities + +Lists the edit ops `script_apply_edits` supports (structured and text), the max edit payload size, and the active guards. No parameters; always visible (no group). + +```python +manage_script_capabilities() +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/manage_script_capabilities) + --- ## Asset Tools @@ -500,7 +519,7 @@ manage_asset(action="delete", path="Assets/OldAsset.asset") ### manage_prefabs -Headless prefab operations. +Prefab assets: headless edits with `modify_contents`, or interactive editing in a prefab stage. ```python # Get prefab info @@ -539,6 +558,8 @@ manage_prefabs( prefab_path="Assets/Prefabs/Player.prefab", create_child={"name": "SpawnPoint", "primitive_type": "Sphere", "position": [0, 2, 0]} ) +# A list creates several children in one save; "source_prefab_path" instead of +# "primitive_type" nests a prefab instance: {"name": "Gun", "source_prefab_path": "Assets/Prefabs/Gun.prefab"} # Set component properties on prefab contents manage_prefabs( @@ -547,6 +568,11 @@ manage_prefabs( target="ChildObject", component_properties={"Rigidbody": {"mass": 5.0}, "MyScript": {"health": 100}} ) + +# Interactive: open a prefab stage, edit with manage_gameobject/manage_components, save, close +manage_prefabs(action="open_prefab_stage", prefab_path="Assets/Prefabs/Enemy.prefab") +manage_prefabs(action="save_prefab_stage") +manage_prefabs(action="close_prefab_stage") # back to the main scene ``` --- @@ -607,7 +633,7 @@ manage_material( ### manage_texture -Create procedural textures. +Create procedural textures. Group: `vfx`. ```python manage_texture( @@ -637,13 +663,24 @@ manage_texture( ) ``` +### manage_shader + +Create, read, update, or delete `.shader` files. Group: `vfx`. `name` is the file name without `.shader`; `path` is its folder under `Assets/`. To use the shader, pass the name declared in its `Shader "..."` line to `manage_material`. + +```python +manage_shader(action="create", name="UnlitTint", path="Assets/Shaders", + contents='Shader "Custom/UnlitTint" { ... }') +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/vfx/manage_shader) + --- ## UI Tools ### manage_ui -Manage Unity UI Toolkit elements: UXML documents, USS stylesheets, UIDocument components, and visual tree inspection. +Manage Unity UI Toolkit elements: UXML documents, USS stylesheets, UIDocument components, and visual tree inspection. Group: `ui`. ```python # Create a UXML file @@ -715,7 +752,7 @@ manage_ui( ### manage_editor -Control Unity Editor state. +Control Unity Editor state, undo/redo. Prefab stages are `manage_prefabs` actions. ```python manage_editor(action="play") # Enter play mode @@ -730,9 +767,8 @@ manage_editor(action="remove_tag", tag_name="OldTag") manage_editor(action="add_layer", layer_name="Projectiles") manage_editor(action="remove_layer", layer_name="OldLayer") -manage_prefabs(action="open_prefab_stage", prefab_path="Assets/Prefabs/Enemy.prefab") -manage_prefabs(action="save_prefab_stage") # Save changes in the open prefab stage -manage_prefabs(action="close_prefab_stage") # Exit prefab editing mode back to main scene +manage_editor(action="undo") # Returns the name of the undone step +manage_editor(action="redo") # Package deployment (no confirmation dialog — designed for LLM-driven iteration) manage_editor(action="deploy_package") # Copy configured MCPForUnity source into installed package @@ -774,8 +810,26 @@ read_console(action="clear") --- +## Build Tools + +### manage_build + +Player builds, platform switching, player settings, build-settings scenes, build profiles, and batch builds. Actions: `build`, `status`, `platform`, `settings`, `scenes`, `profiles`, `batch`, `cancel`. `build` and `batch` return a `job_id`; poll it with `status`, stop it with `cancel`. `platform`, `settings` (with `property`) and `scenes` read when the value is omitted and write when it is given. `profiles` and `profile` need Unity 6+. + +```python +manage_build(action="scenes", scenes='["Assets/Scenes/Menu.unity", "Assets/Scenes/Level1.unity"]') # replaces the list +manage_build(action="build", target="windows64", output_path="Builds/Win/Game.exe", development="true") +manage_build(action="status", job_id="") +``` + +Targets: `windows64`, `osx`, `linux64`, `android`, `ios`, `webgl`, `uwp`, `tvos`, `visionos`. Switching `platform` reimports assets. [Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/core/manage_build) + +--- + ## Testing Tools +Group: `testing`. + ### run_tests Start async test execution. @@ -964,6 +1018,19 @@ manage_camera(action="screenshot", capture_source="scene_view", view_target="Can # Multi-view screenshot (6-angle contact sheet) manage_camera(action="screenshot_multiview", max_resolution=480) +# Batch surround centered on a target: one contact sheet of 6 labeled angles +manage_camera(action="screenshot", batch="surround", view_target="Player", max_resolution=256) + +# Batch orbit: grid of orbit_angles x orbit_elevations tiles around a target +manage_camera(action="screenshot", batch="orbit", view_target="Player", + orbit_angles=8, # azimuth steps (default 8, max 36) + orbit_elevations=[0, 30], # degrees (default [0, 30, -15]) + orbit_distance=10, # optional, auto-fit if omitted + orbit_fov=60, max_resolution=256) + +# Positioned capture: temp camera at a viewpoint (view_rotation overrides the view_target aim) +manage_camera(action="screenshot", view_target="Enemy", view_position=[0, 10, -10], max_resolution=512) + # List all cameras manage_camera(action="list_cameras") ``` @@ -1313,7 +1380,7 @@ manage_physics(action="simulate_step", steps=10, step_size=0.02) ### manage_probuilder -Unified tool for ProBuilder mesh operations. Requires `com.unity.probuilder` package. When available, **prefer ProBuilder over primitive GameObjects** for editable geometry, multi-material faces, or complex shapes. +Unified tool for ProBuilder mesh operations. Group: `probuilder`. Requires `com.unity.probuilder` package. When available, **prefer ProBuilder over primitive GameObjects** for editable geometry, multi-material faces, or complex shapes. **Parameters:** @@ -1582,3 +1649,158 @@ unity_docs(action="lookup", queries="Physics.Raycast,NavMeshAgent,Light2D") unity_docs(action="lookup", query="VolumeProfile", package="com.unity.render-pipelines.universal", pkg_version="17.0") ``` + +--- + +## Animation Tools + +Group: `animation`. + +### manage_animation + +Animator control, AnimatorController authoring, and AnimationClip creation. Actions by prefix: + +- `animator_*` (a scene object's Animator): `get_info`, `get_parameter`, `play`, `crossfade`, `set_parameter`, `set_speed`, `set_enabled` +- `controller_*` (`.controller` assets): `create`, `get_info`, `assign`, `add_state`, `add_transition`, `add_parameter`, `add_layer`, `remove_layer`, `set_layer_weight`, `create_blend_tree_1d`, `create_blend_tree_2d`, `add_blend_tree_child` +- `clip_*` (`.anim` assets): `create`, `get_info`, `add_curve`, `set_curve`, `set_vector_curve`, `create_preset`, `assign`, `add_event`, `remove_event` + +Top-level parameters are `target`, `search_method`, `clip_path` and `controller_path`; everything else goes in `properties` (camelCase or snake_case keys). + +```python +manage_animation(action="controller_add_transition", controller_path="Assets/Animators/Player.controller", + properties={"fromState": "Idle", "toState": "Walk", "hasExitTime": False, + "conditions": [{"parameter": "Speed", "mode": "greater", "threshold": 0.1}]}) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/animation/manage_animation) + +### manage_sprite + +Turns a 2D sprite sheet into sliced sprites, AnimationClips and an AnimatorController. Actions: `get_info`, `slice_sheet`, `setup_clips`, `setup_controller`, `full_setup`. + +- `get_info` returns the sheet's import settings and slices and, for PNG/JPEG, the sheet as an image: count the grid from it before slicing. +- `slice_sheet` cuts a grid (`cols`/`rows` or `frame_width`/`frame_height`) and replaces the sheet's existing slices; `filter_mode` defaults to `point`. +- `full_setup` runs slice, clips, controller. Clip names decide the controller: `idle` is the default state, `walk`/`run` share a `Speed` blend tree, `jump`/`attack`/`hurt`-type names become trigger states. +- Without `overwrite=True`, an existing clip is skipped and an existing controller fails the call. + +```python +manage_sprite(action="full_setup", path="Assets/Sprites/hero.png", cols=6, rows=2, + clips=[{"name": "idle", "start_frame": 0, "end_frame": 5}, + {"name": "walk", "start_frame": 6, "end_frame": 11}], + add_to_scene=True, scene_target="Hero") +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/animation/manage_sprite) + +--- + +## VFX Tools + +### manage_vfx + +ParticleSystem, VisualEffect (VFX Graph), LineRenderer and TrailRenderer components. Group: `vfx`. Action prefixes: `particle_*` (create, modules, bursts, playback), `vfx_*` (VFX Graph assets, exposed properties, events; needs `com.unity.visualeffectgraph`), `line_*` (positions, width, color, and shapes such as `line_create_circle`), `trail_*`; plus `ping`. Settings go in `properties` (camelCase or snake_case keys); `component_index` picks one of several components of the same type. + +```python +manage_vfx(action="particle_create", target="Sparks", properties={"position": [0, 1, 0]}) # creates the GameObject if missing +manage_vfx(action="particle_set_main", target="Sparks", + properties={"startLifetime": 0.8, "startSpeed": 6, "startSize": 0.1, "maxParticles": 200}) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/vfx/manage_vfx) + +--- + +## Scripting Extension Tools + +Group: `scripting_ext`. + +### execute_code + +Runs a C# method body inside the editor, compiled in memory (no script file). `System`, `System.Collections.Generic`, `System.Linq`, `System.Reflection`, `UnityEngine` and `UnityEditor` are imported; `return` a value to get it back. Actions: `execute`, `get_history`, `replay` (`index`), `clear_history`. `safety_checks` (default on) blocks known dangerous calls but is not a sandbox. `compiler`: `auto` (Roslyn if installed, else CodeDom with C# 6), `roslyn`, `codedom`. + +```python +execute_code(action="execute", code="return Selection.gameObjects.Select(g => g.name).ToArray();") +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/scripting_ext/execute_code) + +### manage_scriptable_object + +Creates ScriptableObject assets and edits their serialized fields by property path. `create` takes `type_name` (namespace-qualified), `folder_path`, `asset_name` and optional `overwrite`; `modify` takes `target` (`{"path": ...}` or `{"guid": ...}`) and optional `dry_run`. Both apply `patches`: `{"propertyPath": ..., "op": "set" (default) or "array_resize", "value": ...}`; an object reference uses `"ref": {"guid": ...}` in place of `"value"`. + +```python +manage_scriptable_object(action="modify", target={"path": "Assets/Data/Goblin.asset"}, + patches=[{"propertyPath": "maxHealth", "value": 50}, + {"propertyPath": "drops", "op": "array_resize", "value": 2}]) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/scripting_ext/manage_scriptable_object) + +--- + +## Asset Generation Tools + +Group: `asset_gen`. Bring your own key: provider keys are entered in the Unity editor (MCP for Unity window, Generative tab) and never cross the bridge; `list_providers` shows which are configured. Generation and Sketchfab imports are async: `generate`/`import` return a `job_id`, `status` reports progress and then the imported `assetPath`, and `cancel` stops the job. + +### generate_image + +Text-to-image or image-to-image with fal.ai or OpenRouter, imported as a texture or sprite. Actions: `generate`, `status`, `cancel`, `list_providers`, `list_models`, `refresh_models` (`remove_background` is not supported yet). `generate` takes `provider` (`fal`, `openrouter`), `mode` (`text` or `image`), `prompt` or `image_path`/`image_url`, and optional `model` (from `list_models`), `width`, `height`, `transparent`, `name`, `output_folder`. + +```python +generate_image(action="generate", provider="fal", mode="text", prompt="mossy stone wall, hand-painted", + width=1024, height=1024, output_folder="Assets/Generated") +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/generate_image) + +### generate_model + +Text-to-3D or image-to-3D with Tripo, Meshy or fal, imported as a model. Actions as `generate_image`, without `remove_background`. `generate` takes `provider` (`tripo`, `meshy`, `fal`; fal outputs GLB), `mode`, `prompt` or an image, and optional `format` (`glb`, `fbx`, `obj`, `usdz`), `target_size` (meters, largest side), `texture`, `tier`, `model`. + +```python +generate_model(action="generate", provider="tripo", mode="text", prompt="low-poly treasure chest", + format="glb", target_size=1.0) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/generate_model) + +### generate_audio + +Sound effects and music from a text prompt with fal.ai, imported as an AudioClip. Actions as `generate_model`. `generate` takes `prompt` and optional `model` and `duration` (seconds, clamped per model). + +```python +generate_audio(action="generate", provider="fal", prompt="short bright coin pickup chime", duration=2) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/generate_audio) + +### import_model + +Search Sketchfab and import a downloadable model. Actions: `search` (`query`, `categories`, `downloadable`, `count`, `cursor`), `preview` (`uid`: license, face counts, thumbnails), `import` (`uid`, `target_size`, `name`, `output_folder`), `status`, `cancel`, `list_providers`. + +```python +import_model(action="search", query="wooden barrel", downloadable=True, count=5) +import_model(action="import", uid="", target_size=1.0) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/import_model) + +### import_model_file + +Imports a model file already on disk (`.fbx`, `.obj`, `.glb`, `.gltf`, or a `.zip` holding a model and its sidecar files) through the import pipeline the generators use. No `action` parameter and no key. A rigged FBX/OBJ needs `animation_type="generic"` or `"humanoid"`, or it imports without clips; glTF needs the glTFast package. + +```python +import_model_file(source_path="C:/Exports/robot.fbx", animation_type="humanoid", target_size=1.8) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/import_model_file) + +### blender_bridge + +Drives a running Blender that has the BlenderMCP addon connected (default `127.0.0.1:9876`) directly from Unity. Actions: `status`, `scene_info`, `object_info`, `screenshot`, `run_python`, `import_model`, `compare_screenshot`, `setup_bloom`, `check_updates`, `sync_addon`. `import_model` exports from Blender (glb by default), imports the file, places it at `position`, and scales its largest side to `target_size` meters. + +```python +blender_bridge(action="import_model", object_names=["Robot"], target_size=2.0, position=[0, 0, 0]) +``` + +[Full parameters](https://coplaydev.github.io/unity-mcp/reference/tools/asset_gen/blender_bridge) diff --git a/unity-mcp-skill/references/workflows.md b/unity-mcp-skill/references/workflows.md index 08d56c16d2..1bb196f840 100644 --- a/unity-mcp-skill/references/workflows.md +++ b/unity-mcp-skill/references/workflows.md @@ -107,17 +107,18 @@ batch_execute(commands=[ ]) ``` -### Script Overwrites with `manage_script(action="update")` +### Script Overwrites with `replace_class` -When a generated script needs to be rewritten (e.g., to add auto-wiring logic), use `update` instead of deleting and recreating: +When a generated script needs to be rewritten (e.g., to add auto-wiring logic), replace its class in place instead of deleting and recreating the file: a recreated script gets a new GUID, so components already using it lose their reference. `create_script` refuses to overwrite an existing file, and `manage_script` has no update action. ```python -manage_script( - action="update", - path="Assets/Scripts/MyScript.cs", - contents="using UnityEngine;\n\npublic class MyScript : MonoBehaviour { ... }" +script_apply_edits( + name="MyScript", + path="Assets/Scripts", + edits=[{"op": "replace_class", "className": "MyScript", + "replacement": "public class MyScript : MonoBehaviour { ... }"}] ) -# manage_script update auto-triggers import + compile — just wait and check console +# script_apply_edits auto-triggers import + compile — just wait and check console # Read mcpforunity://editor/state → wait until is_compiling == false read_console(types=["error"], count=10) ``` diff --git a/website/docs/contributing/docs.md b/website/docs/contributing/docs.md index 543122c49e..e2d7c6e947 100644 --- a/website/docs/contributing/docs.md +++ b/website/docs/contributing/docs.md @@ -98,12 +98,12 @@ CI (`.github/workflows/docs-generate.yml`) fails the PR if the committed referen - Renders the full release history into `releases.md`, grouped by minor version, with each release body in a collapsible `
` block. - Replaces the block between `` and `` in the root `README.md` with the latest five releases. -CI keeps both in sync automatically via `.github/workflows/sync-releases.yml`. Triggers are intentionally narrow so the workflow never blocks outsider PRs: +CI keeps both in sync automatically via `.github/workflows/sync-releases.yml`. The `beta` ruleset blocks direct pushes, so the workflow opens a PR into `beta` and merges it itself. Triggers are intentionally narrow so the workflow never blocks outsider PRs: | Trigger | What happens | |---|---| -| `release.{published,edited,unpublished,deleted}` | Sync fires within ~30 seconds and commits directly to `beta` with `[skip ci]`. This is the canonical entry point — the only time the synced files can legitimately go stale. | -| `workflow_dispatch` | Manual escape hatch (re-run after a one-off UI edit, or to backfill). | +| `workflow_dispatch` | The canonical entry point: `release.yml` dispatches the sync right after it creates a release. Also the manual escape hatch (re-run after a one-off UI edit, or to backfill). | +| `release.{published,edited,unpublished,deleted}` | Covers releases a person creates or edits in the GitHub UI. Releases made by `release.yml` never fire it: they are created with `GITHUB_TOKEN`, and events made with that token start no workflows. | **Not triggered on `pull_request`.** A drift check at PR time would fail outsider PRs that edit README for unrelated reasons (typo fix, citation tweak), and the contributor wouldn't have push access to regenerate. The synced files are maintained by the release pipeline, not by PR authors. diff --git a/website/docs/guides/cli-examples.md b/website/docs/guides/cli-examples.md index 89c508c543..41618eb433 100644 --- a/website/docs/guides/cli-examples.md +++ b/website/docs/guides/cli-examples.md @@ -33,6 +33,7 @@ uv pip install -e . | `-t, --timeout` | Timeout seconds | 30 | `UNITY_MCP_TIMEOUT` | | `-f, --format` | Output: text, json, table | text | `UNITY_MCP_FORMAT` | | `-i, --instance` | Target Unity instance | - | `UNITY_MCP_INSTANCE` | +| `-v, --verbose` | Print each command sent to Unity and its raw response to stderr | off | - | ### Core CLI Commands @@ -44,7 +45,7 @@ unity-mcp status # Check server + Unity connection **Instance Management** ```bash unity-mcp instance list # List connected Unity instances -unity-mcp instance set "ProjectName@abc" # Set active instance +unity-mcp --instance "ProjectName@abc" editor play # Target one instance for one call unity-mcp instance current # Show current instance ``` @@ -71,7 +72,7 @@ unity-mcp editor custom-tool "capture_screenshot" --params '{"filename":"shot_01 **Scene Operations** ```bash -unity-mcp scene hierarchy [--limit 20] [--depth 3] +unity-mcp scene hierarchy [--limit 20] [--max-depth 3] unity-mcp scene active unity-mcp scene load "Assets/Scenes/Main.unity" unity-mcp scene save @@ -114,7 +115,7 @@ unity-mcp code search "pattern" "path/to/file.cs" [--max-results 20] **Asset Operations** ```bash -unity-mcp asset search --pattern "*.mat" --path "Assets/Materials" +unity-mcp asset search "*.mat" --path "Assets/Materials" unity-mcp asset info "Assets/Materials/File.mat" unity-mcp asset mkdir "Assets/NewFolder" unity-mcp asset move "Old/Path" "New/Path" @@ -125,7 +126,7 @@ unity-mcp asset move "Old/Path" "New/Path" unity-mcp prefab open "Assets/Prefabs/File.prefab" unity-mcp prefab save unity-mcp prefab close -unity-mcp prefab create "GameObject" --path "Assets/Prefabs" +unity-mcp prefab create "GameObject" "Assets/Prefabs/File.prefab" unity-mcp prefab modify "Assets/Prefabs/File.prefab" --delete-child Child1 unity-mcp prefab modify "Assets/Prefabs/File.prefab" --target Weapon --position "0,1,2" unity-mcp prefab modify "Assets/Prefabs/File.prefab" --set-property "Rigidbody.mass=5" diff --git a/website/docs/guides/cli.md b/website/docs/guides/cli.md index 168976d0e0..9fac634e33 100644 --- a/website/docs/guides/cli.md +++ b/website/docs/guides/cli.md @@ -38,6 +38,7 @@ unity-mcp gameobject find "Player" | `-t, --timeout` | `UNITY_MCP_TIMEOUT` | Timeout in seconds (default: 30) | | `-f, --format` | `UNITY_MCP_FORMAT` | Output format: text, json, table | | `-i, --instance` | `UNITY_MCP_INSTANCE` | Target Unity instance | +| `-v, --verbose` | — | Print each command sent to Unity and its raw response to stderr | ## Command Reference @@ -47,8 +48,8 @@ unity-mcp gameobject find "Player" # List connected Unity instances unity-mcp instance list -# Set active instance -unity-mcp instance set "ProjectName@abc123" +# Target one instance: per call with --instance, or for a whole shell with UNITY_MCP_INSTANCE +unity-mcp --instance "ProjectName@abc123" editor play # Show current instance unity-mcp instance current @@ -59,7 +60,7 @@ unity-mcp instance current ```bash # Get scene hierarchy unity-mcp scene hierarchy -unity-mcp scene hierarchy --limit 20 --depth 3 +unity-mcp scene hierarchy --limit 20 --max-depth 3 # Get active scene info unity-mcp scene active @@ -180,26 +181,6 @@ unity-mcp tool list unity-mcp custom_tool list ``` -#### Screenshot Parameters - -| Option | Type | Description | -|--------|------|-------------| -| `--filename, -f` | string | Output filename (default: timestamp-based) | -| `--supersize, -s` | int | Resolution multiplier 1–4 for file-saved screenshots | -| `--camera-ref` | string | Camera name/path/ID (default: Camera.main) | -| `--include-image` | flag | Return base64 PNG inline in the response | -| `--max-resolution, -r` | int | Max longest-edge pixels (default 640) | -| `--batch, -b` | string | `surround` (6 angles) or `orbit` (configurable grid) | -| `--capture-source` | string | `game_view` (default) or `scene_view` (editor viewport) | -| `--view-target` | string | Target to focus on: GO name/path/ID, or `x,y,z`. Aims camera (game_view) or frames viewport (scene_view) | -| `--view-position` | string | Camera position as `x,y,z` (positioned screenshot, game_view only) | -| `--view-rotation` | string | Camera euler rotation as `x,y,z` (positioned screenshot, game_view only) | -| `--orbit-angles` | int | Number of azimuth steps around target (default 8) | -| `--orbit-elevations` | string | Vertical angles as JSON array, e.g. `[0,30,-15]` (default `[0, 30, -15]`) | -| `--orbit-distance` | float | Camera distance from target in world units (auto-fit if omitted) | -| `--orbit-fov` | float | Camera FOV in degrees (default 60) | -| `--output-dir, -o` | string | Save directory (default: Unity project's `Assets/Screenshots/`) | - ### Testing ```bash @@ -297,7 +278,7 @@ unity-mcp prefab save unity-mcp prefab close # Create from GameObject -unity-mcp prefab create "Player" --path "Assets/Prefabs" +unity-mcp prefab create "Player" "Assets/Prefabs/Player.prefab" # Modify prefab contents (headless, no UI) unity-mcp prefab modify "Assets/Prefabs/Player.prefab" --target Weapon --position "0,1,2" @@ -311,7 +292,7 @@ unity-mcp prefab modify "Assets/Prefabs/Player.prefab" --create-child '{"name":" ```bash # Search assets -unity-mcp asset search --pattern "*.mat" --path "Assets/Materials" +unity-mcp asset search "*.mat" --path "Assets/Materials" # Get asset info unity-mcp asset info "Assets/Materials/Red.mat" @@ -398,6 +379,24 @@ unity-mcp camera screenshot --capture-source scene_view --view-target "Canvas" - unity-mcp camera screenshot-multiview --view-target "Player" --max-resolution 480 ``` +#### Screenshot Options + +`unity-mcp camera screenshot` options (from `--help`): + +| Option | Type | Description | +|--------|------|-------------| +| `--camera-ref` | string | Camera name/path/ID. Omit to capture through the ScreenCapture API | +| `--file-name` | string | Output file name (default: timestamp-based) | +| `--super-size` | int | Resolution multiplier for the saved file | +| `--include-image / --no-include-image` | flag | Also return the PNG inline as base64 | +| `--max-resolution` | int | Longest edge of the inline image in pixels (default 640; 480 per tile with `--batch`) | +| `--capture-source` | string | `game_view` (default) or `scene_view` (editor viewport) | +| `--batch` | string | `surround` (6 angles) or `orbit` (grid around the target) | +| `--view-target` | string | GameObject name/path/ID, or a `[x,y,z]` position. Aims the camera (game_view) or frames the Scene View (scene_view) | +| `--output-folder` | string | Save folder, project-relative or absolute inside the project (default: Editor preference, then `Assets/Screenshots`) | + +`camera screenshot-multiview` takes `--max-resolution`, `--view-target` and `--output-folder`. The MCP tool's `view_position`, `view_rotation` and `orbit_*` settings need `unity-mcp raw manage_camera '{"action": "screenshot", ...}'`. + ### Graphics Operations ```bash @@ -575,7 +574,7 @@ unity-mcp raw manage_packages '{"action": "list_packages"}' | `editor` | `add-layer`, `add-tag`, `console`, `custom-tool`, `deploy`, `menu`, `pause`, `play`, `poll-test`, `redo`, `refresh`, `remove-layer`, `remove-tag`, `restore`, `stop`, `tests`, `tool`, `undo` | | `gameobject` | `create`, `delete`, `duplicate`, `find`, `modify`, `move` | | `graphics` | `bake-cancel`, `bake-clear`, `bake-create-probes`, `bake-create-reflection`, `bake-reflection-probe`, `bake-set-settings`, `bake-settings`, `bake-start`, `bake-status`, `feature-add`, `feature-configure`, `feature-list`, `feature-remove`, `feature-reorder`, `feature-toggle`, `ping`, `pipeline-info`, `pipeline-set-quality`, `pipeline-set-settings`, `pipeline-settings`, `skybox-info`, `skybox-set-ambient`, `skybox-set-fog`, `skybox-set-material`, `skybox-set-properties`, `skybox-set-reflection`, `skybox-set-sun`, `stats`, `stats-debug-mode`, `stats-memory`, `volume-add-effect`, `volume-create`, `volume-create-profile`, `volume-info`, `volume-list-effects`, `volume-remove-effect`, `volume-set-effect`, `volume-set-properties` | -| `instance` | `current`, `list`, `set` | +| `instance` | `current`, `list` | | `lighting` | `create` | | `material` | `assign`, `create`, `info`, `set-color`, `set-property`, `set-renderer-color` | | `packages` | `add`, `add-registry`, `embed`, `info`, `list`, `list-registries`, `ping`, `remove`, `remove-registry`, `resolve`, `search`, `status` | diff --git a/website/docs/reference/cli.md b/website/docs/reference/cli.md index 7e810fd378..b08b9b0d9e 100644 --- a/website/docs/reference/cli.md +++ b/website/docs/reference/cli.md @@ -3,87 +3,95 @@ id: cli slug: /reference/cli title: CLI Reference sidebar_label: CLI -description: The mcp-for-unity command-line interface — invocation, global flags, command groups, and how each maps to the equivalent MCP tool. +description: The unity-mcp command-line interface — invocation, global flags, command groups, and the MCP tools each group calls. --- # CLI Reference -The `mcp-for-unity` CLI is a developer-facing terminal for the same Unity automations the MCP tools expose. Both invoke the same C# `HandleCommand` methods on the Unity side — see [Three-Layer Python Design](/architecture/python-layers) for why both layers exist. +The `unity-mcp` CLI is a developer-facing terminal for the same Unity automations the MCP tools expose. Both invoke the same C# `HandleCommand` methods on the Unity side — see [Three-Layer Python Design](/architecture/python-layers) for why both layers exist. ## Invocation ```bash # Run via uvx (no install) -uvx --from mcpforunityserver mcp-for-unity [args] +uvx --from mcpforunityserver unity-mcp [args] # Run from a Server checkout -cd Server && uv run mcp-for-unity [args] - -# Run via the dedicated CLI entry point (alias) -uvx --from mcpforunityserver unity-mcp [args] +cd Server && uv run unity-mcp [args] ``` +The same package also installs `mcp-for-unity`, which starts the MCP server, not the CLI. + ## How it talks to Unity The CLI uses **HTTP** to the Python server (default `http://127.0.0.1:8080`), regardless of how your MCP clients are configured. The Python server in turn talks to the connected Unity Editor via WebSocket. MCP tools take a similar path via WebSocket directly; CLI commands take HTTP. ## Global flags -| Flag | Default | Meaning | -|---|---|---| -| `--host` | `127.0.0.1` | Python server host to connect to | -| `--port` | `8080` | Python server port | -| `--instance` | (auto) | Target Unity instance (`Name@hash`, hash prefix, or port number) | -| `--format` | `text` | Output format: `text` or `json` | -| `--verbose / -v` | off | Print full request/response payloads | -| `--version` | — | Print CLI version and exit | -| `--help` | — | Show command help | +Global flags go before the command group: `unity-mcp --format json scene active`. + +| Flag | Env variable | Default | Meaning | +|---|---|---|---| +| `--host`, `-h` | `UNITY_MCP_HOST` | `127.0.0.1` | Python server host to connect to | +| `--port`, `-p` | `UNITY_MCP_HTTP_PORT` | `8080` | Python server port | +| `--timeout`, `-t` | `UNITY_MCP_TIMEOUT` | `30` | Command timeout in seconds | +| `--format`, `-f` | `UNITY_MCP_FORMAT` | `text` | Output format: `text`, `json` or `table` | +| `--instance`, `-i` | `UNITY_MCP_INSTANCE` | (auto) | Target Unity instance (hash or `Name@hash`) | +| `--verbose`, `-v` | — | off | Print each command sent to Unity and its raw response to stderr | +| `--version` | — | — | Print CLI version and exit | +| `--help` | — | — | Show command help (`-h` is `--host`, not help) | For multi-instance setups, see [Multi-Instance Routing](/guides/multi-instance). ## Command groups -The CLI mirrors the MCP tool catalog. Each command group wraps one or more `manage_*` tools. +The CLI mirrors the MCP tool catalog. Each command sends one or more MCP tool calls to Unity; the last column names them. Commands marked — only talk to the Python server. -| Group | What it does | Equivalent MCP tool | +| Command | What it does | MCP tool(s) it calls | |---|---|---| -| `mcp-for-unity instance` | List instances, check connection, set active | [`set_active_instance`](/reference/tools/core/set_active_instance) | -| `mcp-for-unity scene` | Load/save/query/edit scenes | [`manage_scene`](/reference/tools/core/manage_scene) | -| `mcp-for-unity gameobject` | Create/transform/delete GameObjects | [`manage_gameobject`](/reference/tools/core/manage_gameobject) | -| `mcp-for-unity component` | Add/remove/configure components | [`manage_components`](/reference/tools/core/manage_components) | -| `mcp-for-unity script` | Create/read/modify C# scripts | [`manage_script`](/reference/tools/core/manage_script) | -| `mcp-for-unity asset` | Asset import/create/modify/search | [`manage_asset`](/reference/tools/core/manage_asset) | -| `mcp-for-unity material` | Material CRUD + shader props | [`manage_material`](/reference/tools/core/manage_material) | -| `mcp-for-unity prefab` | Prefab create/instantiate/unpack | [`manage_prefabs`](/reference/tools/core/manage_prefabs) | -| `mcp-for-unity texture` | Texture create + patterns/gradients | [`manage_texture`](/reference/tools/vfx/manage_texture) | -| `mcp-for-unity shader` | Shader CRUD | [`manage_shader`](/reference/tools/vfx/manage_shader) | -| `mcp-for-unity vfx` | VFX, particle systems, trails | [`manage_vfx`](/reference/tools/vfx/manage_vfx) | -| `mcp-for-unity camera` | Camera + Cinemachine presets | [`manage_camera`](/reference/tools/core/manage_camera) | -| `mcp-for-unity graphics` | Volumes, post-processing, light bake | [`manage_graphics`](/reference/tools/core/manage_graphics) | -| `mcp-for-unity lighting` | Lighting-specific operations | (subset of graphics) | -| `mcp-for-unity physics` | 3D + 2D physics, joints, queries | [`manage_physics`](/reference/tools/core/manage_physics) | -| `mcp-for-unity audio` | Audio operations | (subset of asset) | -| `mcp-for-unity animation` | Animator + AnimationClip | [`manage_animation`](/reference/tools/animation/manage_animation) | -| `mcp-for-unity sprite` | Sprite sheet slicing → clips → Animator controller | [`manage_sprite`](/reference/tools/animation/manage_sprite) | -| `mcp-for-unity ui` | UI Toolkit — UXML/USS/UIDocument | [`manage_ui`](/reference/tools/ui/manage_ui) | -| `mcp-for-unity build` | Player builds across platforms | [`manage_build`](/reference/tools/core/manage_build) | -| `mcp-for-unity editor` | Editor state, play mode, undo/redo | [`manage_editor`](/reference/tools/core/manage_editor) | -| `mcp-for-unity packages` | UPM install/remove/embed | [`manage_packages`](/reference/tools/core/manage_packages) | -| `mcp-for-unity probuilder` | ProBuilder meshes | [`manage_probuilder`](/reference/tools/probuilder/manage_probuilder) | -| `mcp-for-unity profiler` | Profiler session + counters + snapshots | [`manage_profiler`](/reference/tools/profiling/manage_profiler) | -| `mcp-for-unity code` | Execute arbitrary C# in the Editor | [`execute_code`](/reference/tools/scripting_ext/execute_code) | -| `mcp-for-unity batch` | Run multiple operations atomically | [`batch_execute`](/reference/tools/core/batch_execute) | -| `mcp-for-unity tool` | Activate/deactivate tool groups | [`manage_tools`](/reference/tools/core/manage_tools) | -| `mcp-for-unity reflect` | Inspect Unity APIs via reflection | [`unity_reflect`](/reference/tools/docs/unity_reflect) | -| `mcp-for-unity docs` | Fetch Unity docs (ScriptReference, Manual) | [`unity_docs`](/reference/tools/docs/unity_docs) | +| `unity-mcp status` | Check the server connection and list Unity instances | — (server `/health`, `/api/instances`) | +| `unity-mcp instances` | List connected Unity instances | — (server `/api/instances`) | +| `unity-mcp raw` | Send any tool by name with JSON params | The named tool | +| `unity-mcp instance` | List instances, show the one this shell targets (`--instance` / `UNITY_MCP_INSTANCE`) | — (server `/api/instances`) | +| `unity-mcp scene` | Load/save/query/edit scenes | [`manage_scene`](/reference/tools/core/manage_scene) | +| `unity-mcp gameobject` | Find/create/modify/move/duplicate/delete GameObjects | [`manage_gameobject`](/reference/tools/core/manage_gameobject), [`find_gameobjects`](/reference/tools/core/find_gameobjects) (`find`), [`manage_components`](/reference/tools/core/manage_components) (`create --components`) | +| `unity-mcp component` | Add/remove/configure components | [`manage_components`](/reference/tools/core/manage_components) | +| `unity-mcp script` | Create/read/edit/validate/delete C# scripts | [`manage_script`](/reference/tools/core/manage_script) | +| `unity-mcp asset` | Asset import/create/modify/search | [`manage_asset`](/reference/tools/core/manage_asset) | +| `unity-mcp asset-gen` | Generate images, 3D models and audio; import models | [`generate_image`](/reference/tools/asset_gen/generate_image), [`generate_model`](/reference/tools/asset_gen/generate_model), [`generate_audio`](/reference/tools/asset_gen/generate_audio), [`import_model`](/reference/tools/asset_gen/import_model), [`import_model_file`](/reference/tools/asset_gen/import_model_file) | +| `unity-mcp blender` | Talk to a running Blender through the Blender Bridge | [`blender_bridge`](/reference/tools/asset_gen/blender_bridge) | +| `unity-mcp material` | Material CRUD + shader props | [`manage_material`](/reference/tools/core/manage_material) | +| `unity-mcp prefab` | Prefab create/open/save/close, inspect, headless modify | [`manage_prefabs`](/reference/tools/core/manage_prefabs) | +| `unity-mcp texture` | Procedural or image textures, sprites, pixel edits | [`manage_texture`](/reference/tools/vfx/manage_texture) | +| `unity-mcp shader` | Shader CRUD | [`manage_shader`](/reference/tools/vfx/manage_shader) | +| `unity-mcp vfx` | Particle systems, line and trail renderers; `raw` for any `manage_vfx` action | [`manage_vfx`](/reference/tools/vfx/manage_vfx) | +| `unity-mcp camera` | Camera + Cinemachine presets, screenshots | [`manage_camera`](/reference/tools/core/manage_camera) | +| `unity-mcp graphics` | Volumes, render pipeline, light baking, URP features, skybox | [`manage_graphics`](/reference/tools/core/manage_graphics) | +| `unity-mcp lighting` | Create a light GameObject | [`manage_gameobject`](/reference/tools/core/manage_gameobject) + [`manage_components`](/reference/tools/core/manage_components) | +| `unity-mcp physics` | 3D + 2D physics, joints, queries | [`manage_physics`](/reference/tools/core/manage_physics) | +| `unity-mcp audio` | Play, stop or set the volume of an AudioSource | [`manage_components`](/reference/tools/core/manage_components) | +| `unity-mcp animation` | Animator, AnimationClip and AnimatorController | [`manage_animation`](/reference/tools/animation/manage_animation) | +| `unity-mcp sprite` | Sprite sheet slicing → clips → Animator controller | [`manage_sprite`](/reference/tools/animation/manage_sprite) | +| `unity-mcp ui` | Create uGUI Canvas, text, button and image GameObjects | [`manage_gameobject`](/reference/tools/core/manage_gameobject) + [`manage_components`](/reference/tools/core/manage_components) | +| `unity-mcp build` | Player builds across platforms | [`manage_build`](/reference/tools/core/manage_build) | +| `unity-mcp editor` | Play mode, tags/layers, undo/redo, console, refresh, menu items, tests, custom tools | [`manage_editor`](/reference/tools/core/manage_editor), [`read_console`](/reference/tools/core/read_console) (`console`), [`refresh_unity`](/reference/tools/core/refresh_unity) (`refresh`), [`execute_menu_item`](/reference/tools/core/execute_menu_item) (`menu`), [`run_tests`](/reference/tools/testing/run_tests) (`tests`), [`get_test_job`](/reference/tools/testing/get_test_job) (`poll-test`), [`execute_custom_tool`](/reference/tools/core/execute_custom_tool) (`custom-tool`) | +| `unity-mcp packages` | UPM install/remove/embed | [`manage_packages`](/reference/tools/core/manage_packages) | +| `unity-mcp probuilder` | ProBuilder meshes | [`manage_probuilder`](/reference/tools/probuilder/manage_probuilder) | +| `unity-mcp profiler` | Profiler session + counters + snapshots | [`manage_profiler`](/reference/tools/profiling/manage_profiler) | +| `unity-mcp code` | Execute C# in the Editor; read and search script files | [`execute_code`](/reference/tools/scripting_ext/execute_code), [`manage_script`](/reference/tools/core/manage_script) (`read`, `search`) | +| `unity-mcp batch` | Run many commands in one request | [`batch_execute`](/reference/tools/core/batch_execute) | +| `unity-mcp tool` | List the custom tools registered for the active Unity project | — (server `/api/custom-tools`) | +| `unity-mcp custom_tool` | Same as `tool` | — (server `/api/custom-tools`) | +| `unity-mcp reflect` | Inspect Unity APIs via reflection | [`unity_reflect`](/reference/tools/docs/unity_reflect) | +| `unity-mcp docs` | Fetch a Unity ScriptReference page | [`unity_docs`](/reference/tools/docs/unity_docs) (runs in the CLI process) | ## Discovering subcommands and flags Every group supports `--help`: ```bash -mcp-for-unity scene --help -mcp-for-unity scene load --help +unity-mcp scene --help +unity-mcp scene load --help ``` The help text is the authoritative per-command reference — flags, choices, and defaults all live there because the CLI is built on Click and self-describes. diff --git a/website/docs/reference/manifest.md b/website/docs/reference/manifest.md index d032b1829a..a5873a8af1 100644 --- a/website/docs/reference/manifest.md +++ b/website/docs/reference/manifest.md @@ -10,7 +10,7 @@ description: The repo-root manifest.json — what it describes, why it ships, an The `manifest.json` at the repo root describes MCP for Unity as a package — independent of Unity's UPM `package.json` (which lives at `MCPForUnity/package.json`). It's used by MCP marketplaces and aggregators to surface the project's metadata, server invocation, and tool catalog. -If you're adding a new MCP tool, update [the tool registry](/architecture/python-layers) and let CI's drift check fail any stale entry — the generator keeps the docs in sync. The `tools` block in `manifest.json` is a separate, hand-maintained surface (see Notes below). +If you're adding a new MCP tool, update [the tool registry](/architecture/python-layers) and let CI's drift check fail any stale entry — the generator keeps the docs in sync. The `tools` block in `manifest.json` is edited by hand, but `Server/tests/test_manifest_tools.py` fails unless it lists exactly the registered tools (see [`tools`](#tools) below). ## Top-level fields @@ -55,7 +55,7 @@ Tells aggregators how to launch the Python server. A flat array of `{ name, description }` entries listing every MCP tool the server exposes. Aggregators use it for search and category surfaces without having to introspect the live registry. -This list is hand-maintained for now. The authoritative count and metadata live in the Python tool registry — see the [Tool reference](/reference/tools) for the generated catalog with full parameter docs. +The entries are written by hand, but `Server/tests/test_manifest_tools.py` keeps the list equal to the Python tool registry: it fails when a registered tool is missing, when an entry names a tool that is not registered, or when a name appears twice. It compares names only, not descriptions. See the [Tool reference](/reference/tools) for the generated catalog with full parameter docs. ## Notes diff --git a/website/docs/releases.md b/website/docs/releases.md index 5f3d228f46..a495cf6994 100644 --- a/website/docs/releases.md +++ b/website/docs/releases.md @@ -15,8 +15,206 @@ For the canonical changelog with PR links, see [GitHub Releases](https://github. > Auto-generated from the GitHub Releases API by `tools/sync_release_notes.py`. Do not hand-edit — changes will be overwritten on the next sync. +## v10.3 series + +### [v10.3.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.3.0) — 2026-10-04 + +
+Show release notes + +## What's Changed +* chore: sync main (v10.2.0) into beta by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1368 +* perf(discovery): drop the redundant assembly walk from tool discovery too by @ananttheant in https://github.com/CoplayDev/unity-mcp/pull/1369 +* chore: update Unity package to beta version 10.2.1-beta.2 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1377 +* fix: Claude Code registrations invisible to CheckStatus (duplicate path-variant keys, git worktrees) by @crowdedfire in https://github.com/CoplayDev/unity-mcp/pull/1280 +* chore: update Unity package to beta version 10.2.1-beta.3 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1378 +* fix: suppress broker command resends instead of running the work twice by @ananttheant in https://github.com/CoplayDev/unity-mcp/pull/1361 +* chore: update Unity package to beta version 10.2.1-beta.4 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1381 +* fix(read_console): stop the Console window's filters from hiding entries by @KamilDev in https://github.com/CoplayDev/unity-mcp/pull/1342 +* fix(stdio): refresh port discovery before reconnect backoff by @SeojunKim-pumisj in https://github.com/CoplayDev/unity-mcp/pull/1345 +* chore: update Unity package to beta version 10.2.1-beta.5 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1382 +* fix: hardening from the 2026-09-02 beta audit (compile-check on Windows, uv.lock drift, CI stdio pin, auth surface) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1376 +* chore: update Unity package to beta version 10.2.1-beta.6 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1384 +* feat(setup): show git status in the setup window and explain the Git-URL failure by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1383 +* test(setup): look up UpdateDependencyStatus as static too by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1385 +* docs(skill): say where to look when a tool is not in the tool list by @RizgarOzan in https://github.com/CoplayDev/unity-mcp/pull/1406 +* feat: add Trae CN MCP client configurator by @8ddieHu0314 in https://github.com/CoplayDev/unity-mcp/pull/1392 +* fix(roslyn): drop Workspaces-layer formatting so USE_ROSLYN compiles with the installer's DLLs by @comedianhhh in https://github.com/CoplayDev/unity-mcp/pull/1404 +* docs(reference): add unity_docs and unity_reflect examples by @RizgarOzan in https://github.com/CoplayDev/unity-mcp/pull/1401 +* Fix Unity 6000.6 InstanceIDToObjectCompat NotImplementedException by @yuseres in https://github.com/CoplayDev/unity-mcp/pull/1399 +* docs(reference): add run_tests and get_test_job examples by @RizgarOzan in https://github.com/CoplayDev/unity-mcp/pull/1400 +* fix(animation): resolve the Animator on child objects, not just the exact target by @BurakErdemci in https://github.com/CoplayDev/unity-mcp/pull/1337 +* fix: make refresh_unity's compile wait observable across the domain reload by @KamilDev in https://github.com/CoplayDev/unity-mcp/pull/1347 +* fix: serialize Unity.Mathematics structs by their fields, not their swizzles by @RizgarOzan in https://github.com/CoplayDev/unity-mcp/pull/1418 +* feat(clients): auto-configure Pi by @frostebite in https://github.com/CoplayDev/unity-mcp/pull/1414 +* fix(dependencies): resolve Windows Python behind Store App execution aliases (#1324) by @Pikaswelt in https://github.com/CoplayDev/unity-mcp/pull/1389 +* fix: upper-case Roslyn severity with the invariant culture by @RizgarOzan in https://github.com/CoplayDev/unity-mcp/pull/1420 +* fix(tests): recover jobs after reload, bound focus nudges, and fix CI by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1410 +* fix(windows): preserve window state and Unicode focus metadata by @SeojunKim-pumisj in https://github.com/CoplayDev/unity-mcp/pull/1409 +* feat: Blender Bridge — drive BlenderMCP from the Unity Editor (tool + Asset Gen panel) by @Seungpyo1007 in https://github.com/CoplayDev/unity-mcp/pull/1375 +* feat: auto-discover live fal and OpenRouter asset models by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1421 +* Blender Bridge panel: auto-detect the addon and report the real install state by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1422 +* test: end ExecPathBatchShimTests.cs.meta with a newline so Unity 6 can parse it by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1423 +* ci: bring beta's Unity test gate and uv.lock staging to main before 10.2.1 by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1424 +* chore: bump version to 10.3.0 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1425 + +## New Contributors +* @crowdedfire made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1280 +* @SeojunKim-pumisj made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1345 +* @RizgarOzan made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1406 +* @comedianhhh made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1404 +* @yuseres made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1399 +* @BurakErdemci made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1337 +* @frostebite made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1414 +* @Pikaswelt made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1389 +* @Seungpyo1007 made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1375 + +**Full Changelog**: https://github.com/CoplayDev/unity-mcp/compare/v10.2.0...v10.3.0 + +
+ + +## v10.2 series + +### [v10.2.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.2.0) — 2026-09-01 + +
+Show release notes + +## What's Changed +* docs: record multi-agent HTTP behavior in routing docs by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1311 +* chore: sync main (v10.1.2) into beta by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1313 +* ci: license-free compile check on every PR (incl. forks) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1314 +* fix(execute_code): skip phantom CodeDom BOM error from mcs compiler by @beast-ofcourse in https://github.com/CoplayDev/unity-mcp/pull/1250 +* chore: update Unity package to beta version 10.1.3-beta.2 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1315 +* fix: deduplicate CodeDom assembly references by @kpkhxlgy0 in https://github.com/CoplayDev/unity-mcp/pull/1300 +* chore: update Unity package to beta version 10.1.3-beta.3 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1317 +* fix: make stdio bridge command timeout configurable (default 5m) by @andriy-coplay in https://github.com/CoplayDev/unity-mcp/pull/1320 +* chore: update Unity package to beta version 10.1.3-beta.4 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1321 +* chore: drop unmatched MultiEdit permission rule from .claude/settings.json by @KamilDev in https://github.com/CoplayDev/unity-mcp/pull/1352 +* fix: correct Trae MCP config path by @8ddieHu0314 in https://github.com/CoplayDev/unity-mcp/pull/1346 +* docs: fix broken star history charts by @FaintFlower in https://github.com/CoplayDev/unity-mcp/pull/1330 +* chore: update Unity package to beta version 10.1.3-beta.5 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1354 +* fix: MCP window shows 'Not connected' while the stdio bridge is serving requests by @ananttheant in https://github.com/CoplayDev/unity-mcp/pull/1349 +* Fix duplicate-signature false positives on calls at class-member depth by @pattonjh in https://github.com/CoplayDev/unity-mcp/pull/1334 +* fix(build): save assets and scenes before BuildPlayer by @TeapoyY in https://github.com/CoplayDev/unity-mcp/pull/1340 +* chore: update Unity package to beta version 10.1.3-beta.6 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1355 +* fix(build): skip never-saved scenes instead of opening the save modal by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1356 +* chore: update Unity package to beta version 10.1.3-beta.7 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1357 +* fix: read_console severity, screenshot color space, execute_code assembly leak, manage_scene package paths by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1353 +* chore: update Unity package to beta version 10.1.3-beta.8 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1363 +* perf(discovery): use TypeCache instead of scanning every loaded assembly by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1364 +* ci: report Skipped instead of a green check when Unity tests cannot run by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1362 +* chore: update Unity package to beta version 10.1.3-beta.9 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1365 +* fix: reference the Roslyn assemblies from MCPForUnity.Editor.asmdef by @ananttheant in https://github.com/CoplayDev/unity-mcp/pull/1359 +* chore: update Unity package to beta version 10.1.3-beta.10 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1366 +* chore: bump version to 10.2.0 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1367 + +## New Contributors +* @kpkhxlgy0 made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1300 +* @andriy-coplay made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1320 +* @8ddieHu0314 made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1346 +* @FaintFlower made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1330 +* @pattonjh made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1334 +* @TeapoyY made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1340 + +**Full Changelog**: https://github.com/CoplayDev/unity-mcp/compare/v10.1.2...v10.2.0 + +
+ + +## v10.1 series + +### [v10.1.2](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.1.2) — 2026-08-02 + +
+Show release notes + +## What's Changed +* chore: sync main (v10.1.0) into beta by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1271 +* test: report unavailable-pipeline graphics tests as Skipped, not Inconclusive by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1294 +* fix: repair dead camera CLI, add run_tests clear_stuck (#1272), refresh uv.lock by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1293 +* fix: advertise Codex as stdio-only (#1193) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1292 +* fix: redirect stdin from NUL when launching the server on Windows (#1279) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1290 +* fix: trust the pipeline flag when a domain reload is deferred (#1276) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1291 +* chore: update Unity package to beta version 10.1.1-beta.2 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1296 +* docs: fix typos in v8 migration guide by @ananttheant in https://github.com/CoplayDev/unity-mcp/pull/1281 +* fix: restore HTTP transport for Codex (#1193) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1305 +* chore: update Unity package to beta version 10.1.1-beta.3 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1306 +* fix: stop 34 tools forcing an approval prompt on every call (#1288) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1304 +* chore: update Unity package to beta version 10.1.1-beta.4 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1307 +* fix(server): address resources by URI in agent-facing prose by @KamilDev in https://github.com/CoplayDev/unity-mcp/pull/1302 +* ci: retire safe-to-test gate, make skipped Unity checks visible by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1308 +* chore: update Unity package to beta version 10.1.1-beta.5 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1309 +* fix: make component properties reachable on manage_gameobject create by @asavs in https://github.com/CoplayDev/unity-mcp/pull/1298 +* chore: update Unity package to beta version 10.1.1-beta.6 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1310 +* chore: bump version to 10.1.2 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1312 + +## New Contributors +* @ananttheant made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1281 +* @asavs made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1298 + +**Full Changelog**: https://github.com/CoplayDev/unity-mcp/compare/v10.1.0...v10.1.2 + +
+ +### [v10.1.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.1.0) — 2026-07-13 + +
+Show release notes + +## What's Changed +* chore: sync main (v10.0.2) into beta by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1268 +* feat(asset-gen): AI audio generation + shared model panel (hardened) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1264 +* chore: update Unity package to beta version 10.0.3-beta.2 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1269 +* chore: bump version to 10.1.0 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1270 + +**Full Changelog**: https://github.com/CoplayDev/unity-mcp/compare/v10.0.2...v10.1.0 + +
+ + ## v10.0 series +### [v10.0.2](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.0.2) — 2026-07-13 + +
+Show release notes + +## What's Changed +* chore: sync main (v10.0.0) into beta by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1225 +* feat(editor): embed Ocean brand mark in Editor UI + setup polish by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1231 +* chore: update Unity package to beta version 10.0.1-beta.2 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1233 +* fix(editor): skip shutdown cleanup in batch mode so CI instances don't stop the interactive server by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1235 +* chore: update Unity package to beta version 10.0.1-beta.3 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1242 +* fix(screenshot): restore pause state after composited capture by @KamilDev in https://github.com/CoplayDev/unity-mcp/pull/1230 +* fix: preserve multi-line message body in read_console by @GallopingDino in https://github.com/CoplayDev/unity-mcp/pull/1227 +* chore: update Unity package to beta version 10.0.1-beta.4 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1245 +* docs(server): guide agents to read resources by URI, not name by @Lothsahn in https://github.com/CoplayDev/unity-mcp/pull/1244 +* chore: update Unity package to beta version 10.0.1-beta.5 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1246 +* fix(server): stop silent cross-instance routing when multiple editors are connected (#1023) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1236 +* chore: update Unity package to beta version 10.0.1-beta.6 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1249 +* fix(manage_build): stop forcing PVRTC texture compression on Android via subtarget by @beast-ofcourse in https://github.com/CoplayDev/unity-mcp/pull/1253 +* chore: update Unity package to beta version 10.0.1-beta.7 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1257 +* fix: stop tearing down healthy HTTP sessions on transient probe misses (#1207) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1237 +* fix(editor): survive domain reloads in HTTP auto-start and reload-resume (#1229) by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1234 +* chore: update Unity package to beta version 10.0.1-beta.8 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1259 +* fix(stdio): recover from a reload-orphaned socket instead of hanging by @mertakdut in https://github.com/CoplayDev/unity-mcp/pull/1217 +* chore: update Unity package to beta version 10.0.1-beta.9 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1260 +* feat(asset-gen): add animation_type rig mode to import_model_file by @Scriptwonder in https://github.com/CoplayDev/unity-mcp/pull/1262 +* chore: update Unity package to beta version 10.0.1-beta.10 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1263 +* chore: bump version to 10.0.2 by @github-actions[bot] in https://github.com/CoplayDev/unity-mcp/pull/1267 + +## New Contributors +* @GallopingDino made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1227 +* @Lothsahn made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1244 +* @beast-ofcourse made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1253 +* @mertakdut made their first contribution in https://github.com/CoplayDev/unity-mcp/pull/1217 + +**Full Changelog**: https://github.com/CoplayDev/unity-mcp/compare/v10.0.0...v10.0.2 + +
+ ### [v10.0.0](https://github.com/CoplayDev/unity-mcp/releases/tag/v10.0.0) — 2026-06-30