chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
---
|
||||
id: claude-code-cli
|
||||
slug: /guides/claude-code-cli
|
||||
title: Install or Repair Claude Code CLI
|
||||
sidebar_label: Claude Code CLI
|
||||
description: Install or repair the Claude Code CLI (claude) so MCP for Unity can launch it.
|
||||
---
|
||||
|
||||
# Install or Repair Claude Code CLI
|
||||
|
||||
You need the Claude Code CLI (`claude`) available on your system.
|
||||
|
||||
:::caution Switching transport requires a restart
|
||||
If you change from `http` to `stdio` (or vice versa) in the MCP for Unity window, **restart Claude Code** for it to pick up the change.
|
||||
:::
|
||||
|
||||
## Recommended (native installers)
|
||||
|
||||
**macOS / Linux / WSL:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://claude.ai/install.sh | bash
|
||||
claude doctor
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
|
||||
```powershell
|
||||
irm https://claude.ai/install.ps1 | iex
|
||||
claude doctor
|
||||
```
|
||||
|
||||
## Alternative: npm via NVM (installs under `~/.nvm`)
|
||||
|
||||
```bash
|
||||
# Install / select a Node version
|
||||
nvm install v21.7.1
|
||||
nvm use v21.7.1
|
||||
|
||||
# Install Claude Code CLI into this Node's global prefix
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
|
||||
# Verify it's under NVM
|
||||
which claude
|
||||
claude --version
|
||||
```
|
||||
|
||||
## Alternative: npm with system / Homebrew Node
|
||||
|
||||
```bash
|
||||
# If you don't have Node yet (macOS):
|
||||
brew install node
|
||||
|
||||
# Install Claude Code CLI globally
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
|
||||
# Verify it's on PATH (typical: /opt/homebrew/bin/claude or /usr/local/bin/claude)
|
||||
which claude
|
||||
claude --version
|
||||
```
|
||||
|
||||
## macOS PATH gotcha
|
||||
|
||||
On macOS, Unity launched from Finder / Hub may not inherit your shell PATH. If `claude` isn't found:
|
||||
|
||||
- **Either** launch Hub from Terminal (so PATH propagates),
|
||||
- **or** use the MCP for Unity window's **"Choose Claude Install Location"** to set the absolute path.
|
||||
|
||||
## Related troubleshooting
|
||||
|
||||
- macOS dyld ICU library errors: see [Common Setup Problems → macOS Claude CLI dyld error](/guides/troubleshooting#macos-claude-cli-fails-to-start-dyld-icu-library-not-loaded)
|
||||
- "Claude Not Found" in the Register button: see the FAQ in [Common Setup Problems](/guides/troubleshooting#faq--claude-code)
|
||||
@@ -0,0 +1,252 @@
|
||||
---
|
||||
title: CLI Examples
|
||||
sidebar_label: CLI Examples
|
||||
description: Worked examples of using MCP for Unity in CLI mode.
|
||||
---
|
||||
|
||||
# Unity MCP (CLI Mode)
|
||||
|
||||
We use Unity MCP via **CLI commands** instead of MCP server connection. This avoids the reconnection issues that occur when Unity restarts.
|
||||
|
||||
### Why CLI Instead of MCP Connection?
|
||||
|
||||
- MCP connection breaks when Unity restarts
|
||||
- `/mcp reconnect` requires human intervention
|
||||
- CLI works directly via HTTP to the MCP server - no persistent connection needed
|
||||
- Claude can call CLI commands autonomously without reconnection issues
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
cd Server # In unity-mcp repo
|
||||
pip install -e .
|
||||
# Or with uv:
|
||||
uv pip install -e .
|
||||
```
|
||||
|
||||
### Global Options
|
||||
|
||||
| Option | Description | Default | Env Variable |
|
||||
|--------|-------------|---------|--------------|
|
||||
| `-h, --host` | Server host | 127.0.0.1 | `UNITY_MCP_HOST` |
|
||||
| `-p, --port` | Server port | 8080 | `UNITY_MCP_HTTP_PORT` |
|
||||
| `-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` |
|
||||
|
||||
### Core CLI Commands
|
||||
|
||||
**Status & Connection**
|
||||
```bash
|
||||
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 current # Show current instance
|
||||
```
|
||||
|
||||
**Editor Control**
|
||||
```bash
|
||||
unity-mcp editor play|pause|stop # Control play mode
|
||||
unity-mcp editor console [--clear] # Get/clear console logs
|
||||
unity-mcp editor refresh [--compile] # Refresh assets
|
||||
unity-mcp editor menu "Edit/Project Settings..." # Execute menu item
|
||||
unity-mcp editor add-tag "TagName" # Add tag
|
||||
unity-mcp editor add-layer "LayerName" # Add layer
|
||||
unity-mcp editor tests --mode PlayMode [--async]
|
||||
unity-mcp editor poll-test <job_id> [--wait 60] [--details]
|
||||
unity-mcp --instance "MyProject@abc123" editor play # Target a specific instance
|
||||
```
|
||||
|
||||
**Custom Tools**
|
||||
```bash
|
||||
unity-mcp tool list
|
||||
unity-mcp custom_tool list
|
||||
unity-mcp editor custom-tool "bake_lightmaps"
|
||||
unity-mcp editor custom-tool "capture_screenshot" --params '{"filename":"shot_01","width":1920,"height":1080}'
|
||||
```
|
||||
|
||||
**Scene Operations**
|
||||
```bash
|
||||
unity-mcp scene hierarchy [--limit 20] [--depth 3]
|
||||
unity-mcp scene active
|
||||
unity-mcp scene load "Assets/Scenes/Main.unity"
|
||||
unity-mcp scene save
|
||||
unity-mcp --format json scene hierarchy
|
||||
```
|
||||
|
||||
**Screenshots** (via `camera` command):
|
||||
```bash
|
||||
unity-mcp camera screenshot --file-name "capture"
|
||||
unity-mcp camera screenshot --camera-ref "MainCam" --include-image --max-resolution 512
|
||||
unity-mcp camera screenshot --batch surround --max-resolution 256
|
||||
unity-mcp camera screenshot --batch orbit --view-target "Player"
|
||||
unity-mcp camera screenshot --capture-source scene_view --view-target "Canvas" --include-image
|
||||
unity-mcp camera screenshot-multiview --view-target "Player" --max-resolution 480
|
||||
```
|
||||
|
||||
**GameObject Operations**
|
||||
```bash
|
||||
unity-mcp gameobject find "Name" [--method by_tag|by_name|by_layer|by_component]
|
||||
unity-mcp gameobject create "Name" [--primitive Cube] [--position X Y Z]
|
||||
unity-mcp gameobject modify "Name" [--position X Y Z] [--rotation X Y Z]
|
||||
unity-mcp gameobject delete "Name" [--force]
|
||||
unity-mcp gameobject duplicate "Name"
|
||||
```
|
||||
|
||||
**Component Operations**
|
||||
```bash
|
||||
unity-mcp component add "GameObject" ComponentType
|
||||
unity-mcp component remove "GameObject" ComponentType
|
||||
unity-mcp component set "GameObject" Component property value
|
||||
```
|
||||
|
||||
**Script Operations**
|
||||
```bash
|
||||
unity-mcp script create "ScriptName" --path "Assets/Scripts"
|
||||
unity-mcp script read "Assets/Scripts/File.cs"
|
||||
unity-mcp script delete "Assets/Scripts/File.cs" [--force]
|
||||
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 info "Assets/Materials/File.mat"
|
||||
unity-mcp asset mkdir "Assets/NewFolder"
|
||||
unity-mcp asset move "Old/Path" "New/Path"
|
||||
```
|
||||
|
||||
**Prefab Operations**
|
||||
```bash
|
||||
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 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"
|
||||
```
|
||||
|
||||
**Material Operations**
|
||||
```bash
|
||||
unity-mcp material create "Assets/Materials/File.mat"
|
||||
unity-mcp material set-color "File.mat" R G B
|
||||
unity-mcp material assign "File.mat" "GameObject"
|
||||
```
|
||||
|
||||
**Shader Operations**
|
||||
```bash
|
||||
unity-mcp shader create "Name" --path "Assets/Shaders"
|
||||
unity-mcp shader read "Assets/Shaders/Custom.shader"
|
||||
unity-mcp shader update "Assets/Shaders/Custom.shader" --file local.shader
|
||||
unity-mcp shader delete "Assets/Shaders/File.shader" [--force]
|
||||
```
|
||||
|
||||
**VFX Operations**
|
||||
```bash
|
||||
unity-mcp vfx particle info|play|stop|pause|restart|clear "Name"
|
||||
unity-mcp vfx line info "Name"
|
||||
unity-mcp vfx line create-line "Name" --start X Y Z --end X Y Z
|
||||
unity-mcp vfx line create-circle "Name" --radius N
|
||||
unity-mcp vfx trail info|set-time|clear "Name" [time]
|
||||
```
|
||||
|
||||
**Camera Operations**
|
||||
```bash
|
||||
unity-mcp camera ping # Check Cinemachine
|
||||
unity-mcp camera list # List all cameras
|
||||
unity-mcp camera create --name "Cam" --preset follow --follow "Player"
|
||||
unity-mcp camera set-target "Cam" --follow "Player" --look-at "Enemy"
|
||||
unity-mcp camera set-lens "Cam" --fov 60 --near 0.1
|
||||
unity-mcp camera set-priority "Cam" --priority 15
|
||||
unity-mcp camera set-body "Cam" --body-type "CinemachineFollow"
|
||||
unity-mcp camera set-aim "Cam" --aim-type "CinemachineRotationComposer"
|
||||
unity-mcp camera set-noise "Cam" --amplitude 1.5 --frequency 0.5
|
||||
unity-mcp camera ensure-brain --blend-style "EaseInOut" --blend-duration 1.5
|
||||
unity-mcp camera force "Cam" # Force Brain to use camera
|
||||
unity-mcp camera release # Release override
|
||||
```
|
||||
|
||||
**Graphics Operations**
|
||||
```bash
|
||||
# Volumes
|
||||
unity-mcp graphics volume-create --name "PostFX" --global
|
||||
unity-mcp graphics volume-add-effect --target "PostFX" --effect "Bloom"
|
||||
unity-mcp graphics volume-set-effect --target "PostFX" --effect "Bloom" -p intensity 1.5
|
||||
unity-mcp graphics volume-info --target "PostFX"
|
||||
# Pipeline
|
||||
unity-mcp graphics pipeline-info
|
||||
unity-mcp graphics pipeline-set-quality --level "High"
|
||||
# Baking
|
||||
unity-mcp graphics bake-start [--sync]
|
||||
unity-mcp graphics bake-status
|
||||
unity-mcp graphics bake-create-probes --spacing 5
|
||||
# Stats
|
||||
unity-mcp graphics stats
|
||||
unity-mcp graphics stats-memory
|
||||
# URP Features
|
||||
unity-mcp graphics feature-list
|
||||
unity-mcp graphics feature-add --type "ScreenSpaceAmbientOcclusion"
|
||||
# Skybox
|
||||
unity-mcp graphics skybox-info
|
||||
unity-mcp graphics skybox-set-fog --enable --mode ExponentialSquared --density 0.02
|
||||
unity-mcp graphics skybox-set-sun --target "DirectionalLight"
|
||||
```
|
||||
|
||||
**Package Operations**
|
||||
```bash
|
||||
unity-mcp packages list # List installed
|
||||
unity-mcp packages search "cinemachine" # Search registry
|
||||
unity-mcp packages info "com.unity.cinemachine" # Details
|
||||
unity-mcp packages add "com.unity.cinemachine" # Install
|
||||
unity-mcp packages add "com.unity.cinemachine@4.1.1" # Specific version
|
||||
unity-mcp packages remove "com.unity.cinemachine" [--force]
|
||||
unity-mcp packages embed "com.unity.cinemachine" # Local editing
|
||||
unity-mcp packages resolve # Re-resolve
|
||||
unity-mcp packages list-registries
|
||||
unity-mcp packages add-registry "Name" --url URL -s "com.example"
|
||||
```
|
||||
|
||||
**Texture Operations**
|
||||
```bash
|
||||
unity-mcp texture create "Assets/Textures/Red.png" --color "1,0,0,1"
|
||||
unity-mcp texture create "Assets/Textures/Check.png" --pattern checkerboard --width 256 --height 256
|
||||
unity-mcp texture sprite "Assets/Sprites/Player.png" --width 32 --height 32 --ppu 16
|
||||
unity-mcp texture modify "Assets/Textures/Img.png" --set-pixels '{"x":0,"y":0,"width":16,"height":16,"color":[1,0,0,1]}'
|
||||
unity-mcp texture delete "Assets/Textures/Old.png" [--force]
|
||||
```
|
||||
|
||||
**Lighting & UI**
|
||||
```bash
|
||||
unity-mcp lighting create "Name" --type Point|Spot [--intensity N] [--position X Y Z]
|
||||
unity-mcp ui create-canvas "Name"
|
||||
unity-mcp ui create-text "Name" --parent "Canvas" --text "Content"
|
||||
unity-mcp ui create-button "Name" --parent "Canvas" --text "Label"
|
||||
```
|
||||
|
||||
**Batch Operations**
|
||||
```bash
|
||||
unity-mcp batch run commands.json [--parallel] [--fail-fast]
|
||||
unity-mcp batch inline '[{"tool": "manage_scene", "params": {...}}]'
|
||||
unity-mcp batch template > commands.json
|
||||
```
|
||||
|
||||
**Raw Access (Any Tool)**
|
||||
```bash
|
||||
unity-mcp raw tool_name 'JSON_params'
|
||||
unity-mcp raw manage_scene '{"action":"get_active"}'
|
||||
unity-mcp raw manage_camera '{"action":"screenshot","include_image":true}'
|
||||
unity-mcp raw manage_graphics '{"action":"volume_get_info","target":"PostProcessing"}'
|
||||
unity-mcp raw manage_packages '{"action":"list_packages"}'
|
||||
```
|
||||
|
||||
### Note on MCP Server
|
||||
|
||||
The MCP HTTP server still needs to be running for CLI to work. Here is an example to run the server manually on Mac:
|
||||
```bash
|
||||
/opt/homebrew/bin/uvx --no-cache --refresh --from /XXX/unity-mcp/Server mcp-for-unity --transport http --http-url http://localhost:8080
|
||||
```
|
||||
@@ -0,0 +1,560 @@
|
||||
# Unity MCP CLI Usage Guide
|
||||
|
||||
The Unity MCP CLI provides command-line access to control the Unity Editor through the Model Context Protocol. It currently only supports local HTTP.
|
||||
|
||||
Note: Some tools are still experimental and might fail under some circumstances. Please submit an issue to help us make it better.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cd Server
|
||||
pip install -e .
|
||||
# Or with uv:
|
||||
uv pip install -e .
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Check connection
|
||||
unity-mcp status
|
||||
|
||||
# List Unity instances
|
||||
unity-mcp instance list
|
||||
|
||||
# Get scene hierarchy
|
||||
unity-mcp scene hierarchy
|
||||
|
||||
# Find a GameObject
|
||||
unity-mcp gameobject find "Player"
|
||||
```
|
||||
|
||||
## Global Options
|
||||
|
||||
| Option | Env Variable | Description |
|
||||
|--------|--------------|-------------|
|
||||
| `-h, --host` | `UNITY_MCP_HOST` | Server host (default: 127.0.0.1) |
|
||||
| `-p, --port` | `UNITY_MCP_HTTP_PORT` | Server port (default: 8080) |
|
||||
| `-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 |
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Instance Management
|
||||
|
||||
```bash
|
||||
# List connected Unity instances
|
||||
unity-mcp instance list
|
||||
|
||||
# Set active instance
|
||||
unity-mcp instance set "ProjectName@abc123"
|
||||
|
||||
# Show current instance
|
||||
unity-mcp instance current
|
||||
```
|
||||
|
||||
### Scene Operations
|
||||
|
||||
```bash
|
||||
# Get scene hierarchy
|
||||
unity-mcp scene hierarchy
|
||||
unity-mcp scene hierarchy --limit 20 --depth 3
|
||||
|
||||
# Get active scene info
|
||||
unity-mcp scene active
|
||||
|
||||
# Load/save scenes
|
||||
unity-mcp scene load "Assets/Scenes/Main.unity"
|
||||
unity-mcp scene save
|
||||
|
||||
# Screenshots (use camera command)
|
||||
unity-mcp camera screenshot
|
||||
unity-mcp camera screenshot --file-name "level_preview"
|
||||
unity-mcp camera screenshot --camera-ref "SecondCamera" --include-image
|
||||
unity-mcp camera screenshot --batch surround --max-resolution 256
|
||||
unity-mcp camera screenshot --batch orbit --view-target "Player"
|
||||
unity-mcp camera screenshot --capture-source scene_view --view-target "Canvas" --include-image
|
||||
unity-mcp camera screenshot-multiview --view-target "Player" --max-resolution 480
|
||||
```
|
||||
|
||||
### GameObject Operations
|
||||
|
||||
```bash
|
||||
# Find GameObjects
|
||||
unity-mcp gameobject find "Player"
|
||||
unity-mcp gameobject find "Enemy" --method by_tag
|
||||
|
||||
# Create GameObjects
|
||||
unity-mcp gameobject create "NewCube" --primitive Cube
|
||||
unity-mcp gameobject create "Empty" --position 0 5 0
|
||||
|
||||
# Modify GameObjects
|
||||
unity-mcp gameobject modify "Cube" --position 1 2 3 --rotation 0 45 0
|
||||
|
||||
# Delete/duplicate
|
||||
unity-mcp gameobject delete "OldObject" --force
|
||||
unity-mcp gameobject duplicate "Template"
|
||||
```
|
||||
|
||||
### Component Operations
|
||||
|
||||
```bash
|
||||
# Add component
|
||||
unity-mcp component add "Player" Rigidbody
|
||||
|
||||
# Remove component
|
||||
unity-mcp component remove "Player" Rigidbody
|
||||
|
||||
# Set property
|
||||
unity-mcp component set "Player" Rigidbody mass 10
|
||||
```
|
||||
|
||||
### Script Operations
|
||||
|
||||
```bash
|
||||
# Create script
|
||||
unity-mcp script create "PlayerController" --path "Assets/Scripts"
|
||||
|
||||
# Read script
|
||||
unity-mcp script read "Assets/Scripts/Player.cs"
|
||||
|
||||
# Delete script
|
||||
unity-mcp script delete "Assets/Scripts/Old.cs" --force
|
||||
```
|
||||
|
||||
### Code Search
|
||||
|
||||
```bash
|
||||
# Search with regex
|
||||
unity-mcp code search "class.*Player" "Assets/Scripts/Player.cs"
|
||||
unity-mcp code search "TODO|FIXME" "Assets/Scripts/Utils.cs"
|
||||
unity-mcp code search "void Update" "Assets/Scripts/Game.cs" --max-results 20
|
||||
```
|
||||
|
||||
### Shader Operations
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Editor Controls
|
||||
|
||||
```bash
|
||||
# Play mode
|
||||
unity-mcp editor play
|
||||
unity-mcp editor pause
|
||||
unity-mcp editor stop
|
||||
|
||||
# Refresh assets
|
||||
unity-mcp editor refresh
|
||||
unity-mcp editor refresh --compile
|
||||
|
||||
# Console
|
||||
unity-mcp editor console
|
||||
unity-mcp editor console --clear
|
||||
|
||||
# Tags and layers
|
||||
unity-mcp editor add-tag "Enemy"
|
||||
unity-mcp editor add-layer "Projectiles"
|
||||
|
||||
# Menu items
|
||||
unity-mcp editor menu "Edit/Project Settings..."
|
||||
|
||||
# Custom tools
|
||||
unity-mcp editor custom-tool "MyBuildTool"
|
||||
unity-mcp editor custom-tool "Deploy" --params '{"target": "Android"}'
|
||||
|
||||
# List custom tools for the active Unity project
|
||||
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
|
||||
# Run tests synchronously
|
||||
unity-mcp editor tests --mode EditMode
|
||||
|
||||
# Run tests asynchronously
|
||||
unity-mcp editor tests --mode PlayMode --async
|
||||
|
||||
# Poll test job
|
||||
unity-mcp editor poll-test <job_id>
|
||||
unity-mcp editor poll-test <job_id> --wait 60 --details
|
||||
```
|
||||
|
||||
### Material Operations
|
||||
|
||||
```bash
|
||||
# Create material
|
||||
unity-mcp material create "Assets/Materials/Red.mat"
|
||||
|
||||
# Set color
|
||||
unity-mcp material set-color "Assets/Materials/Red.mat" 1 0 0
|
||||
|
||||
# Assign to object
|
||||
unity-mcp material assign "Assets/Materials/Red.mat" "Cube"
|
||||
```
|
||||
|
||||
### VFX Operations
|
||||
|
||||
Note: VFX Graph tooling is tested against com.unity.visualeffectgraph 12.1.13. Install VFX Graph and use URP/HDRP (set the Render Pipeline Asset) to avoid Unity warnings; 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 Operations
|
||||
|
||||
Note: Requires com.unity.probuilder package installed 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 Operations
|
||||
|
||||
```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 Operations
|
||||
|
||||
```bash
|
||||
# Open prefab for editing
|
||||
unity-mcp prefab open "Assets/Prefabs/Player.prefab"
|
||||
|
||||
# Save and close
|
||||
unity-mcp prefab save
|
||||
unity-mcp prefab close
|
||||
|
||||
# Create from GameObject
|
||||
unity-mcp prefab create "Player" --path "Assets/Prefabs"
|
||||
|
||||
# Modify prefab contents (headless, no UI)
|
||||
unity-mcp prefab modify "Assets/Prefabs/Player.prefab" --target Weapon --position "0,1,2"
|
||||
unity-mcp prefab modify "Assets/Prefabs/Player.prefab" --delete-child Child1 --delete-child "Turret/Barrel"
|
||||
unity-mcp prefab modify "Assets/Prefabs/Player.prefab" --set-property "Rigidbody.mass=5"
|
||||
unity-mcp prefab modify "Assets/Prefabs/Player.prefab" --add-component BoxCollider
|
||||
unity-mcp prefab modify "Assets/Prefabs/Player.prefab" --create-child '{"name":"Spawn","primitive_type":"Sphere"}'
|
||||
```
|
||||
|
||||
### Asset Operations
|
||||
|
||||
```bash
|
||||
# Search assets
|
||||
unity-mcp asset search --pattern "*.mat" --path "Assets/Materials"
|
||||
|
||||
# Get asset info
|
||||
unity-mcp asset info "Assets/Materials/Red.mat"
|
||||
|
||||
# Create folder
|
||||
unity-mcp asset mkdir "Assets/NewFolder"
|
||||
|
||||
# Move/rename
|
||||
unity-mcp asset move "Assets/Old.mat" "Assets/Materials/"
|
||||
```
|
||||
|
||||
### Animation Operations
|
||||
|
||||
```bash
|
||||
# Play animation state
|
||||
unity-mcp animation play "Player" "Run"
|
||||
|
||||
# Set animator parameter
|
||||
unity-mcp animation set-parameter "Player" Speed 1.5
|
||||
unity-mcp animation set-parameter "Player" IsRunning true
|
||||
```
|
||||
|
||||
### Audio Operations
|
||||
|
||||
```bash
|
||||
# Play audio
|
||||
unity-mcp audio play "AudioPlayer"
|
||||
|
||||
# Stop audio
|
||||
unity-mcp audio stop "AudioPlayer"
|
||||
|
||||
# Set volume
|
||||
unity-mcp audio volume "AudioPlayer" 0.5
|
||||
```
|
||||
|
||||
### Lighting Operations
|
||||
|
||||
```bash
|
||||
# Create light
|
||||
unity-mcp lighting create "NewLight" --type Point --position 0 5 0
|
||||
unity-mcp lighting create "Spotlight" --type Spot --intensity 2
|
||||
```
|
||||
|
||||
### UI Operations
|
||||
|
||||
```bash
|
||||
# Create canvas
|
||||
unity-mcp ui create-canvas "MainCanvas"
|
||||
|
||||
# Create text
|
||||
unity-mcp ui create-text "Title" --parent "MainCanvas" --text "Hello World"
|
||||
|
||||
# Create button
|
||||
unity-mcp ui create-button "StartBtn" --parent "MainCanvas" --text "Start"
|
||||
|
||||
# Create image
|
||||
unity-mcp ui create-image "Background" --parent "MainCanvas"
|
||||
```
|
||||
|
||||
### Camera Operations
|
||||
|
||||
```bash
|
||||
unity-mcp camera ping # Check Cinemachine availability
|
||||
unity-mcp camera list # List all cameras
|
||||
unity-mcp camera create --name "Cam" --preset follow --follow "Player"
|
||||
unity-mcp camera set-target "Cam" --follow "Player" --look-at "Enemy"
|
||||
unity-mcp camera set-lens "Cam" --fov 60 --near 0.1 --far 1000
|
||||
unity-mcp camera set-priority "Cam" --priority 15
|
||||
unity-mcp camera set-body "Cam" --body-type "CinemachineFollow"
|
||||
unity-mcp camera set-aim "Cam" --aim-type "CinemachineRotationComposer"
|
||||
unity-mcp camera set-noise "Cam" --amplitude 1.5 --frequency 0.5
|
||||
unity-mcp camera add-extension "Cam" CinemachineConfiner3D
|
||||
unity-mcp camera ensure-brain --blend-style "EaseInOut" --blend-duration 1.5
|
||||
unity-mcp camera brain-status
|
||||
unity-mcp camera force "Cam" # Force Brain to use camera
|
||||
unity-mcp camera release # Release override
|
||||
unity-mcp camera screenshot --file-name "capture" --super-size 2
|
||||
unity-mcp camera screenshot --batch orbit --view-target "Player" --max-resolution 256
|
||||
unity-mcp camera screenshot --capture-source scene_view --view-target "Canvas" --include-image
|
||||
unity-mcp camera screenshot-multiview --view-target "Player" --max-resolution 480
|
||||
```
|
||||
|
||||
### Graphics Operations
|
||||
|
||||
```bash
|
||||
# Volumes
|
||||
unity-mcp graphics volume-create --name "PostFX" --global
|
||||
unity-mcp graphics volume-add-effect --target "PostFX" --effect "Bloom"
|
||||
unity-mcp graphics volume-set-effect --target "PostFX" --effect "Bloom" -p intensity 1.5
|
||||
unity-mcp graphics volume-info --target "PostFX"
|
||||
unity-mcp graphics volume-list-effects
|
||||
|
||||
# Render Pipeline
|
||||
unity-mcp graphics pipeline-info
|
||||
unity-mcp graphics pipeline-set-quality --level "High"
|
||||
unity-mcp graphics pipeline-set-settings -s renderScale 1.5
|
||||
|
||||
# Light Baking
|
||||
unity-mcp graphics bake-start [--sync]
|
||||
unity-mcp graphics bake-status
|
||||
unity-mcp graphics bake-cancel
|
||||
unity-mcp graphics bake-settings
|
||||
unity-mcp graphics bake-create-probes --spacing 5
|
||||
unity-mcp graphics bake-create-reflection --resolution 512
|
||||
|
||||
# Stats & Debug
|
||||
unity-mcp graphics stats
|
||||
unity-mcp graphics stats-memory
|
||||
unity-mcp graphics stats-debug-mode --mode "Wireframe"
|
||||
|
||||
# URP Renderer Features
|
||||
unity-mcp graphics feature-list
|
||||
unity-mcp graphics feature-add --type "ScreenSpaceAmbientOcclusion"
|
||||
unity-mcp graphics feature-configure --name "SSAO" -p Intensity 1.5
|
||||
unity-mcp graphics feature-toggle --name "SSAO" --active|--inactive
|
||||
|
||||
# Skybox & Environment
|
||||
unity-mcp graphics skybox-info
|
||||
unity-mcp graphics skybox-set-material --material "Assets/Materials/Sky.mat"
|
||||
unity-mcp graphics skybox-set-ambient --mode Flat --color "0.2,0.2,0.3"
|
||||
unity-mcp graphics skybox-set-fog --enable --mode ExponentialSquared --density 0.02
|
||||
unity-mcp graphics skybox-set-reflection --intensity 1.0 --bounces 2
|
||||
unity-mcp graphics skybox-set-sun --target "DirectionalLight"
|
||||
```
|
||||
|
||||
### Package Operations
|
||||
|
||||
```bash
|
||||
unity-mcp packages ping # Check package manager
|
||||
unity-mcp packages list # List installed packages
|
||||
unity-mcp packages search "cinemachine" # Search registry
|
||||
unity-mcp packages info "com.unity.cinemachine" # Package details
|
||||
unity-mcp packages add "com.unity.cinemachine" # Install package
|
||||
unity-mcp packages add "com.unity.cinemachine@4.1.1" # Specific version
|
||||
unity-mcp packages remove "com.unity.cinemachine" [--force]
|
||||
unity-mcp packages embed "com.unity.cinemachine" # Embed for local editing
|
||||
unity-mcp packages resolve # Force re-resolution
|
||||
unity-mcp packages status <job_id> # Check async op
|
||||
unity-mcp packages list-registries
|
||||
unity-mcp packages add-registry "Name" --url URL -s "com.example"
|
||||
unity-mcp packages remove-registry "Name"
|
||||
```
|
||||
|
||||
### Texture Operations
|
||||
|
||||
```bash
|
||||
unity-mcp texture create "Assets/Textures/Red.png" --color "1,0,0,1"
|
||||
unity-mcp texture create "Assets/Textures/Check.png" --pattern checkerboard --width 256 --height 256
|
||||
unity-mcp texture create "Assets/Textures/Img.png" --image-path "/path/to/source.png"
|
||||
unity-mcp texture sprite "Assets/Sprites/Player.png" --width 32 --height 32 --ppu 16
|
||||
unity-mcp texture modify "Assets/Textures/Img.png" --set-pixels '{"x":0,"y":0,"width":16,"height":16,"color":[1,0,0,1]}'
|
||||
unity-mcp texture delete "Assets/Textures/Old.png" [--force]
|
||||
# Patterns: checkerboard, stripes, stripes_h, stripes_v, stripes_diag, dots, grid, brick
|
||||
```
|
||||
|
||||
### Raw Commands
|
||||
|
||||
For any MCP tool not covered by dedicated commands:
|
||||
|
||||
```bash
|
||||
unity-mcp raw manage_scene '{"action": "get_hierarchy", "max_nodes": 100}'
|
||||
unity-mcp raw read_console '{"count": 20}'
|
||||
unity-mcp raw manage_camera '{"action": "screenshot", "include_image": true}'
|
||||
unity-mcp raw manage_graphics '{"action": "volume_get_info", "target": "PostProcessing"}'
|
||||
unity-mcp raw manage_packages '{"action": "list_packages"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Command Reference
|
||||
|
||||
| Group | Subcommands |
|
||||
|-------|-------------|
|
||||
| `instance` | `list`, `set`, `current` |
|
||||
| `scene` | `hierarchy`, `active`, `load`, `save`, `create`, `build-settings` |
|
||||
| `code` | `read`, `search` |
|
||||
| `gameobject` | `find`, `create`, `modify`, `delete`, `duplicate`, `move` |
|
||||
| `component` | `add`, `remove`, `set`, `modify` |
|
||||
| `script` | `create`, `read`, `delete`, `edit`, `validate` |
|
||||
| `shader` | `create`, `read`, `update`, `delete` |
|
||||
| `editor` | `play`, `pause`, `stop`, `refresh`, `console`, `menu`, `tool`, `add-tag`, `remove-tag`, `add-layer`, `remove-layer`, `tests`, `poll-test`, `custom-tool` |
|
||||
| `asset` | `search`, `info`, `create`, `delete`, `duplicate`, `move`, `rename`, `import`, `mkdir` |
|
||||
| `prefab` | `open`, `close`, `save`, `create`, `modify` |
|
||||
| `material` | `info`, `create`, `set-color`, `set-property`, `assign`, `set-renderer-color` |
|
||||
| `camera` | `ping`, `list`, `create`, `set-target`, `set-lens`, `set-priority`, `set-body`, `set-aim`, `set-noise`, `add-extension`, `remove-extension`, `ensure-brain`, `brain-status`, `set-blend`, `force`, `release`, `screenshot`, `screenshot-multiview` |
|
||||
| `graphics` | `ping`, `volume-create`, `volume-add-effect`, `volume-set-effect`, `volume-remove-effect`, `volume-info`, `volume-set-properties`, `volume-list-effects`, `volume-create-profile`, `pipeline-info`, `pipeline-settings`, `pipeline-set-quality`, `pipeline-set-settings`, `bake-start`, `bake-cancel`, `bake-status`, `bake-clear`, `bake-settings`, `bake-set-settings`, `bake-reflection-probe`, `bake-create-probes`, `bake-create-reflection`, `stats`, `stats-memory`, `stats-debug-mode`, `feature-list`, `feature-add`, `feature-remove`, `feature-configure`, `feature-reorder`, `feature-toggle`, `skybox-info`, `skybox-set-material`, `skybox-set-properties`, `skybox-set-ambient`, `skybox-set-fog`, `skybox-set-reflection`, `skybox-set-sun` |
|
||||
| `packages` | `ping`, `list`, `search`, `info`, `add`, `remove`, `embed`, `resolve`, `status`, `list-registries`, `add-registry`, `remove-registry` |
|
||||
| `texture` | `create`, `sprite`, `modify`, `delete` |
|
||||
| `vfx particle` | `info`, `play`, `stop`, `pause`, `restart`, `clear` |
|
||||
| `vfx line` | `info`, `set-positions`, `create-line`, `create-circle`, `clear` |
|
||||
| `vfx trail` | `info`, `set-time`, `clear` |
|
||||
| `vfx` | `raw` (access all 60+ actions) |
|
||||
| `probuilder` | `create-shape`, `create-poly`, `info`, `raw` (access all 35+ actions) |
|
||||
| `batch` | `run`, `inline`, `template` |
|
||||
| `animation` | `play`, `set-parameter` |
|
||||
| `audio` | `play`, `stop`, `volume` |
|
||||
| `lighting` | `create` |
|
||||
| `tool` | `list` |
|
||||
| `custom_tool` | `list` |
|
||||
| `ui` | `create-canvas`, `create-text`, `create-button`, `create-image` |
|
||||
|
||||
---
|
||||
|
||||
## Output Formats
|
||||
|
||||
```bash
|
||||
# Text (default) - human readable
|
||||
unity-mcp scene hierarchy
|
||||
|
||||
# JSON - for scripting
|
||||
unity-mcp --format json scene hierarchy
|
||||
|
||||
# Table - structured display
|
||||
unity-mcp --format table instance list
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Set defaults via environment:
|
||||
|
||||
```bash
|
||||
export UNITY_MCP_HOST=192.168.1.100
|
||||
export UNITY_MCP_HTTP_PORT=8080
|
||||
export UNITY_MCP_FORMAT=json
|
||||
export UNITY_MCP_INSTANCE=MyProject@abc123
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
|
||||
```bash
|
||||
# Check server status
|
||||
unity-mcp status
|
||||
|
||||
# Verify Unity is running with MCP plugin
|
||||
# Check Unity console for MCP connection messages
|
||||
```
|
||||
|
||||
### Common Errors
|
||||
|
||||
| Error | Solution |
|
||||
|-------|----------|
|
||||
| Cannot connect to server | Ensure Unity MCP server is running |
|
||||
| Unknown command type | Unity plugin may not support this tool |
|
||||
| Timeout | Increase timeout with `-t 60` |
|
||||
@@ -0,0 +1,300 @@
|
||||
# MCP Client Configurators
|
||||
|
||||
This guide explains how MCP client configurators work in this repo and how to add a new one.
|
||||
|
||||
It covers:
|
||||
|
||||
- **Typical JSON-file clients** (Cursor, VSCode GitHub Copilot, VSCode Insiders, GitHub Copilot CLI, Windsurf, Kiro, Trae, Antigravity 2.0, Antigravity IDE, etc.).
|
||||
- **Special clients** like **Claude CLI**, **Codex**, and **OpenClaw** that require custom logic.
|
||||
- **How to add a new configurator class** so it shows up automatically in the MCP for Unity window.
|
||||
|
||||
## Quick example: JSON-file configurator
|
||||
|
||||
For most clients you just need a small class like this:
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MCPForUnity.Editor.Models;
|
||||
|
||||
namespace MCPForUnity.Editor.Clients.Configurators
|
||||
{
|
||||
public class MyClientConfigurator : JsonFileMcpConfigurator
|
||||
{
|
||||
public MyClientConfigurator() : base(new McpClient
|
||||
{
|
||||
name = "My Client",
|
||||
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".myclient", "mcp.json"),
|
||||
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".myclient", "mcp.json"),
|
||||
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".myclient", "mcp.json"),
|
||||
})
|
||||
{ }
|
||||
|
||||
public override IList<string> GetInstallationSteps() => new List<string>
|
||||
{
|
||||
"Open My Client and go to MCP settings",
|
||||
"Open or create the mcp.json file at the path above",
|
||||
"Click Configure in MCP for Unity (or paste the manual JSON snippet)",
|
||||
"Restart My Client"
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How the configurator system works
|
||||
|
||||
At a high level:
|
||||
|
||||
- **`IMcpClientConfigurator`** (`MCPForUnity/Editor/Clients/IMcpClientConfigurator.cs`)
|
||||
- Contract for all MCP client configurators.
|
||||
- Handles status detection, auto-configure, manual snippet, and installation steps.
|
||||
|
||||
- **Base classes** (`MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs`)
|
||||
- **`McpClientConfiguratorBase`**
|
||||
- Common properties and helpers.
|
||||
- **`JsonFileMcpConfigurator`**
|
||||
- For JSON-based config files (most clients).
|
||||
- Implements `CheckStatus`, `Configure`, and `GetManualSnippet` using `ConfigJsonBuilder`.
|
||||
- **`CodexMcpConfigurator`**
|
||||
- For Codex-style TOML config files.
|
||||
- **`ClaudeCliMcpConfigurator`**
|
||||
- For CLI-driven clients like Claude Code (register/unregister via CLI, not JSON files).
|
||||
|
||||
- **`McpClient` model** (`MCPForUnity/Editor/Models/McpClient.cs`)
|
||||
- Holds the per-client configuration:
|
||||
- `name`
|
||||
- `windowsConfigPath`, `macConfigPath`, `linuxConfigPath`
|
||||
- Status and several **JSON-config flags** (used by `JsonFileMcpConfigurator`):
|
||||
- `IsVsCodeLayout` – VS Code-style layout (`servers` root, `type` field, etc.).
|
||||
- `SupportsHttpTransport` – whether the client supports HTTP transport.
|
||||
- `EnsureEnvObject` – ensure an `env` object exists.
|
||||
- `StripEnvWhenNotRequired` – remove `env` when not needed.
|
||||
- `HttpUrlProperty` – which property holds the HTTP URL (e.g. `"url"` vs `"serverUrl"`).
|
||||
- `DefaultUnityFields` – key/value pairs like `{ "disabled": false }` applied when missing.
|
||||
|
||||
- **Auto-discovery** (`McpClientRegistry`)
|
||||
- `McpClientRegistry.All` uses `TypeCache.GetTypesDerivedFrom<IMcpClientConfigurator>()` to find configurators.
|
||||
- A configurator appears automatically if:
|
||||
- It is a **public, non-abstract class**.
|
||||
- It has a **public parameterless constructor**.
|
||||
- No extra registration list is required.
|
||||
|
||||
---
|
||||
|
||||
## Typical JSON-file clients
|
||||
|
||||
Most MCP clients use a JSON config file that defines one or more MCP servers. Examples:
|
||||
|
||||
- **Cursor** – `JsonFileMcpConfigurator` (global `~/.cursor/mcp.json`).
|
||||
- **VSCode GitHub Copilot** – `JsonFileMcpConfigurator` with `IsVsCodeLayout = true`.
|
||||
- **VSCode Insiders GitHub Copilot** – `JsonFileMcpConfigurator` with `IsVsCodeLayout = true` and Insider-specific `Code - Insiders/User/mcp.json` paths.
|
||||
- **GitHub Copilot CLI** – `JsonFileMcpConfigurator` with standard HTTP transport.
|
||||
- **Windsurf** – `JsonFileMcpConfigurator` with Windsurf-specific flags (`HttpUrlProperty = "serverUrl"`, `DefaultUnityFields["disabled"] = false`, etc.).
|
||||
- **Kiro**, **Trae**, **Antigravity 2.0** (`~/.gemini/config/mcp_config.json` after the 2.x migration), **Antigravity IDE** (separate `~/.gemini/antigravity-ide/mcp_config.json` — the IDE build did not migrate) – JSON configs with project-specific paths and flags.
|
||||
|
||||
All of these follow the same pattern:
|
||||
|
||||
1. **Subclass `JsonFileMcpConfigurator`.**
|
||||
2. **Provide a `McpClient` instance** in the constructor with:
|
||||
- A user-friendly `name`.
|
||||
- OS-specific config paths.
|
||||
- Any JSON behavior flags as needed.
|
||||
3. **Override `GetInstallationSteps`** to describe how users open or edit the config.
|
||||
4. Rely on **base implementations** for:
|
||||
- `CheckStatus` – reads and validates the JSON config; can auto-rewrite to match Unity MCP.
|
||||
- `Configure` – writes/rewrites the config file.
|
||||
- `GetManualSnippet` – builds a JSON snippet using `ConfigJsonBuilder`.
|
||||
|
||||
### JSON behavior controlled by `McpClient`
|
||||
|
||||
`JsonFileMcpConfigurator` relies on the fields on `McpClient`:
|
||||
|
||||
- **HTTP vs stdio**
|
||||
- `SupportsHttpTransport` + `EditorPrefs.UseHttpTransport` decide whether to configure
|
||||
- `url` / `serverUrl` (HTTP), or
|
||||
- `command` + `args` (stdio with `uvx`).
|
||||
- **URL property name**
|
||||
- `HttpUrlProperty` (default `"url"`) selects which JSON property to use for HTTP urls.
|
||||
- Example: Windsurf and the two Antigravity clients use `"serverUrl"`.
|
||||
- **VS Code layout**
|
||||
- `IsVsCodeLayout = true` switches config structure to a VS Code compatible layout.
|
||||
- **Env object and default fields**
|
||||
- `EnsureEnvObject` / `StripEnvWhenNotRequired` control an `env` block.
|
||||
- `DefaultUnityFields` adds client-specific fields if they are missing (e.g. `disabled: false`).
|
||||
|
||||
All of this logic is centralized in **`ConfigJsonBuilder`**, so most JSON-based clients **do not need to override** `GetManualSnippet`.
|
||||
|
||||
---
|
||||
|
||||
## Special clients
|
||||
|
||||
Some clients cannot be handled by the generic JSON configurator alone.
|
||||
|
||||
### Codex (TOML-based)
|
||||
|
||||
- Uses **`CodexMcpConfigurator`**.
|
||||
- Reads and writes a **TOML** config (usually `~/.codex/config.toml`).
|
||||
- Uses `CodexConfigHelper` to:
|
||||
- Parse the existing TOML.
|
||||
- Check for a matching Unity MCP server configuration.
|
||||
- Write/patch the Codex server block.
|
||||
- The `CodexConfigurator` class:
|
||||
- Only needs to supply a `McpClient` with TOML config paths.
|
||||
- Inherits the Codex-specific status and configure behavior from `CodexMcpConfigurator`.
|
||||
|
||||
### Claude Code (CLI-based)
|
||||
|
||||
- Uses **`ClaudeCliMcpConfigurator`**.
|
||||
- Configuration is stored **internally by the Claude CLI**, not in a JSON file.
|
||||
- `CheckStatus` and `Configure` are implemented in the base class using `claude mcp ...` commands:
|
||||
- `CheckStatus` calls `claude mcp list` to detect if `UnityMCP` is registered.
|
||||
- `Configure` toggles register/unregister via `claude mcp add/remove UnityMCP`.
|
||||
- The `ClaudeCodeConfigurator` class:
|
||||
- Only needs a `McpClient` with a `name`.
|
||||
- Overrides `GetInstallationSteps` with CLI-specific instructions.
|
||||
|
||||
### Claude Desktop (JSON with restrictions)
|
||||
|
||||
- Uses **`JsonFileMcpConfigurator`**, but only supports **stdio transport**.
|
||||
- `ClaudeDesktopConfigurator`:
|
||||
- Sets `SupportsHttpTransport = false` in `McpClient`.
|
||||
- Declares `SupportedTransports => StdioOnly`. `ClientConfigurationService` reads `SupportedTransports` and, via `ConfigureWithTransportCoercion` / `CoerceTransportFor`, temporarily coerces the transport pref to stdio before calling `Configure()` — so users with HTTP toggled globally still get a working stdio entry written without a thrown error. The coercion applies to any configurator whose `SupportedTransports` excludes the user's current transport; Claude Desktop is just the only one declaring stdio-only today.
|
||||
|
||||
### OpenClaw (plugin-based)
|
||||
|
||||
- Uses a custom configurator (`OpenClawConfigurator`) because OpenClaw MCP is plugin-driven.
|
||||
- Config file path is `~/.openclaw/openclaw.json`.
|
||||
- Unity MCP is configured through `plugins.entries.openclaw-mcp-bridge.config.servers.unityMCP`.
|
||||
- When Unity MCP is set to HTTP, the bridge expects the MCP JSON-RPC endpoint URL (`http://127.0.0.1:<port>/mcp`), not just the HTTP base URL.
|
||||
- When Unity MCP is set to stdio, the configurator writes a `uvx ... mcp-for-unity --transport stdio` subprocess entry.
|
||||
- The bridge exposes a single proxy tool such as `unityMCP__call`, which then forwards to Unity MCP tool names.
|
||||
- OpenClaw support follows the currently selected MCP for Unity transport (via `openclaw-mcp-bridge`).
|
||||
|
||||
---
|
||||
|
||||
## Adding a new MCP client (typical JSON case)
|
||||
|
||||
This is the most common scenario: your MCP client uses a JSON file to configure servers.
|
||||
|
||||
### 1. Choose the base class
|
||||
|
||||
- Use **`JsonFileMcpConfigurator`** if your client reads a JSON config file.
|
||||
- Consider **`CodexMcpConfigurator`** only if you are integrating a TOML-based client like Codex.
|
||||
- Consider **`ClaudeCliMcpConfigurator`** only if your client exposes a CLI command to manage MCP servers.
|
||||
|
||||
### 2. Create the configurator class
|
||||
|
||||
Create a new file under:
|
||||
|
||||
```text
|
||||
MCPForUnity/Editor/Clients/Configurators
|
||||
```
|
||||
|
||||
Name it something like:
|
||||
|
||||
```text
|
||||
MyClientConfigurator.cs
|
||||
```
|
||||
|
||||
Inside, follow the existing pattern (e.g. `CursorConfigurator`, `WindsurfConfigurator`, `KiroConfigurator`):
|
||||
|
||||
- **Namespace** must be:
|
||||
- `MCPForUnity.Editor.Clients.Configurators`
|
||||
- **Class**:
|
||||
- `public class MyClientConfigurator : JsonFileMcpConfigurator`
|
||||
- **Constructor**:
|
||||
- Public, **parameterless**, and call `base(new McpClient { ... })`.
|
||||
- Set at least:
|
||||
- `name = "My Client"`
|
||||
- `windowsConfigPath = ...`
|
||||
- `macConfigPath = ...`
|
||||
- `linuxConfigPath = ...`
|
||||
- Optionally set flags:
|
||||
- `IsVsCodeLayout = true` for VS Code-style config.
|
||||
- `HttpUrlProperty = "serverUrl"` if your client expects `serverUrl`.
|
||||
- `EnsureEnvObject` / `StripEnvWhenNotRequired` based on env handling.
|
||||
- `DefaultUnityFields = { { "disabled", false }, ... }` for client-specific defaults.
|
||||
|
||||
Because the constructor is parameterless and public, **`McpClientRegistry` will auto-discover this configurator** with no extra registration.
|
||||
|
||||
### 3. Add installation steps
|
||||
|
||||
Override `GetInstallationSteps` to tell users how to configure the client:
|
||||
|
||||
- Where to find or create the JSON config file.
|
||||
- Which menu path opens the MCP settings.
|
||||
- Whether they should rely on the **Configure** button or copy-paste the manual JSON.
|
||||
|
||||
Look at `CursorConfigurator`, `VSCodeConfigurator`, `VSCodeInsidersConfigurator`, `KiroConfigurator`, `TraeConfigurator`, `AntigravityConfigurator`, or `AntigravityIdeConfigurator` for phrasing.
|
||||
|
||||
### 4. Rely on the base JSON logic
|
||||
|
||||
Unless your client has very unusual behavior, you typically **do not need to override**:
|
||||
|
||||
- `CheckStatus`
|
||||
- `Configure`
|
||||
- `GetManualSnippet`
|
||||
|
||||
The base `JsonFileMcpConfigurator`:
|
||||
|
||||
- Detects missing or mismatched config.
|
||||
- Optionally rewrites config to match Unity MCP.
|
||||
- Builds a JSON snippet with **correct HTTP vs stdio settings**, using `ConfigJsonBuilder`.
|
||||
|
||||
Only override these methods if your client has constraints that cannot be expressed via `McpClient` flags.
|
||||
|
||||
### 5. Verify in Unity
|
||||
|
||||
After adding your configurator class:
|
||||
|
||||
1. Open Unity and the **MCP for Unity** window.
|
||||
2. Your client should appear in the list, sorted by display name (`McpClient.name`).
|
||||
3. Use **Check Status** to verify:
|
||||
- Missing config files show as `Not Configured`.
|
||||
- Existing files with matching server settings show as `Configured`.
|
||||
4. Click **Configure** to auto-write the config file.
|
||||
5. Restart your MCP client and confirm it connects to Unity.
|
||||
|
||||
---
|
||||
|
||||
## Adding a custom (non-JSON) client
|
||||
|
||||
If your MCP client doesnt store configuration as a JSON file, you likely need a custom base class.
|
||||
|
||||
### Codex-style TOML client
|
||||
|
||||
- Subclass **`CodexMcpConfigurator`**.
|
||||
- Provide TOML paths via `McpClient` (similar to `CodexConfigurator`).
|
||||
- Override `GetInstallationSteps` to describe how to open/edit the TOML.
|
||||
|
||||
The Codex-specific status and configure logic is already implemented in the base class.
|
||||
|
||||
### CLI-managed client (Claude-style)
|
||||
|
||||
- Subclass **`ClaudeCliMcpConfigurator`**.
|
||||
- Provide a `McpClient` with a `name`.
|
||||
- Override `GetInstallationSteps` with the CLI flow.
|
||||
|
||||
The base class:
|
||||
|
||||
- Locates the CLI binary using `MCPServiceLocator.Paths`.
|
||||
- Uses `ExecPath.TryRun` to call `mcp list`, `mcp add`, and `mcp remove`.
|
||||
- Implements `Configure` as a toggle between register and unregister.
|
||||
|
||||
Use this only if the client exposes an official CLI for managing MCP servers.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
- **For most MCP clients**, you only need to:
|
||||
- Create a `JsonFileMcpConfigurator` subclass in `Editor/Clients/Configurators`.
|
||||
- Provide a `McpClient` with paths and flags.
|
||||
- Override `GetInstallationSteps`.
|
||||
- **Special cases** like Codex (TOML) and Claude Code (CLI) have dedicated base classes.
|
||||
- **No manual registration** is needed: `McpClientRegistry` auto-discovers all configurators with a public parameterless constructor.
|
||||
|
||||
Following these patterns keeps all MCP client integrations consistent and lets users configure everything from the MCP for Unity window with minimal friction.
|
||||
@@ -0,0 +1,310 @@
|
||||
# Adding Custom Tools to MCP for Unity
|
||||
|
||||
MCP for Unity makes it easy to extend your AI assistant with custom capabilities. Using C# attributes and reflection, the system automatically discovers and registers your tools—no manual configuration needed.
|
||||
|
||||
This guide will walk you through creating your own tools, from simple synchronous operations to complex long-running tasks that survive Unity's domain reloads.
|
||||
|
||||
---
|
||||
|
||||
# Quick Start Guide
|
||||
|
||||
Let's get you up and running with your first custom tool.
|
||||
|
||||
## Step 1: Create Your Tool Handler
|
||||
|
||||
First, create a C# file in any `Editor/` folder within your Unity project. **The Editor folder is crucial**—we scan Editor assemblies for tools, so placing your code elsewhere means it won't be discovered.
|
||||
|
||||
Each tool is a static class with two key ingredients:
|
||||
1. The `[McpForUnityTool]` attribute that tells the system "Hey, I'm a tool!"
|
||||
2. A `HandleCommand(JObject)` method that does the actual work
|
||||
|
||||
You can also define a nested `Parameters` class with `[ToolParameter]` attributes. This gives your AI assistant helpful descriptions and lets it know which parameters are required.
|
||||
|
||||
```csharp
|
||||
using Newtonsoft.Json.Linq;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.Editor.Tools;
|
||||
|
||||
namespace MyProject.Editor.CustomTools
|
||||
{
|
||||
[McpForUnityTool("my_custom_tool")]
|
||||
public static class MyCustomTool
|
||||
{
|
||||
public class Parameters
|
||||
{
|
||||
[ToolParameter("Value to process")]
|
||||
public string param1 { get; set; }
|
||||
|
||||
[ToolParameter("Optional integer payload", Required = false)]
|
||||
public int? param2 { get; set; }
|
||||
}
|
||||
|
||||
public static object HandleCommand(JObject @params)
|
||||
{
|
||||
var parameters = @params.ToObject<Parameters>();
|
||||
|
||||
if (string.IsNullOrEmpty(parameters.param1))
|
||||
{
|
||||
return new ErrorResponse("param1 is required");
|
||||
}
|
||||
|
||||
DoSomethingAmazing(parameters.param1, parameters.param2);
|
||||
|
||||
return new SuccessResponse("Custom tool executed successfully!", new
|
||||
{
|
||||
parameters.param1,
|
||||
parameters.param2
|
||||
});
|
||||
}
|
||||
|
||||
private static void DoSomethingAmazing(string param1, int? param2)
|
||||
{
|
||||
// Your implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Step 2: Refresh Your MCP Client
|
||||
|
||||
Once you've created your tool, you'll need to let your AI assistant know about it. While the MCP server can dynamically register new tools, not all clients pick up these changes automatically.
|
||||
|
||||
**The easiest approach:** Disconnect and reconnect to the MCP server in your client. This forces a fresh tool discovery.
|
||||
|
||||
**If that doesn't work:** Some clients (like Windsurf) may need you to remove and reconfigure the MCP for Unity server entirely. It's a bit more work, but it guarantees your new tools will appear.
|
||||
|
||||
## Step 3: List and Call Your Tool from the CLI
|
||||
|
||||
If you want to use the CLI directly, list custom tools for the active Unity project:
|
||||
|
||||
```bash
|
||||
unity-mcp tool list
|
||||
unity-mcp custom_tool list
|
||||
```
|
||||
|
||||
Then call your tool by name:
|
||||
|
||||
```bash
|
||||
unity-mcp editor custom-tool "my_custom_tool"
|
||||
unity-mcp editor custom-tool "my_custom_tool" --params '{"param1":"value"}'
|
||||
```
|
||||
|
||||
## Complete Example: Screenshot Tool
|
||||
|
||||
### C# Handler (`Assets/Editor/ScreenShots/CaptureScreenshotTool.cs`)
|
||||
|
||||
```csharp
|
||||
using System.IO;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using MCPForUnity.Editor.Tools;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
|
||||
namespace MyProject.Editor.CustomTools
|
||||
{
|
||||
[McpForUnityTool(
|
||||
name: "capture_screenshot",
|
||||
Description = "Capture screenshots in Unity, saving them as PNGs"
|
||||
)]
|
||||
public static class CaptureScreenshotTool
|
||||
{
|
||||
// Define parameters as a nested class for clarity
|
||||
public class Parameters
|
||||
{
|
||||
[ToolParameter("Screenshot filename without extension, e.g., screenshot_01")]
|
||||
public string filename { get; set; }
|
||||
|
||||
[ToolParameter("Width of the screenshot in pixels", Required = false)]
|
||||
public int? width { get; set; }
|
||||
|
||||
[ToolParameter("Height of the screenshot in pixels", Required = false)]
|
||||
public int? height { get; set; }
|
||||
}
|
||||
|
||||
public static object HandleCommand(JObject @params)
|
||||
{
|
||||
// Parse parameters
|
||||
var parameters = @params.ToObject<Parameters>();
|
||||
|
||||
if (string.IsNullOrEmpty(parameters.filename))
|
||||
{
|
||||
return new ErrorResponse("filename is required");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int width = parameters.width ?? Screen.width;
|
||||
int height = parameters.height ?? Screen.height;
|
||||
|
||||
string absolutePath = Path.Combine(Application.dataPath, "Screenshots",
|
||||
parameters.filename + ".png");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(absolutePath));
|
||||
|
||||
// Find camera
|
||||
Camera camera = Camera.main ?? Object.FindFirstObjectByType<Camera>();
|
||||
if (camera == null)
|
||||
{
|
||||
return new ErrorResponse("No camera found in the scene");
|
||||
}
|
||||
|
||||
// Capture screenshot
|
||||
RenderTexture rt = new RenderTexture(width, height, 24);
|
||||
camera.targetTexture = rt;
|
||||
camera.Render();
|
||||
|
||||
RenderTexture.active = rt;
|
||||
Texture2D screenshot = new Texture2D(width, height, TextureFormat.RGB24, false);
|
||||
screenshot.ReadPixels(new Rect(0, 0, width, height), 0, 0);
|
||||
screenshot.Apply();
|
||||
|
||||
// Cleanup
|
||||
camera.targetTexture = null;
|
||||
RenderTexture.active = null;
|
||||
Object.DestroyImmediate(rt);
|
||||
|
||||
// Save
|
||||
byte[] bytes = screenshot.EncodeToPNG();
|
||||
File.WriteAllBytes(absolutePath, bytes);
|
||||
Object.DestroyImmediate(screenshot);
|
||||
|
||||
return new SuccessResponse($"Screenshot saved to {absolutePath}", new
|
||||
{
|
||||
path = absolutePath,
|
||||
width = width,
|
||||
height = height
|
||||
});
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return new ErrorResponse($"Failed to capture screenshot: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Long-Running (Polled) Tools
|
||||
|
||||
Some operations—like running tests, baking lightmaps, or building players—take time and might even trigger Unity domain reloads. For these cases, you'll want a **polled tool**.
|
||||
|
||||
Here's how it works: Your tool starts the work and returns a "pending" signal. Behind the scenes, the Python middleware automatically polls Unity for updates until the job completes (or times out after 10 minutes).
|
||||
|
||||
### Setting Up Polling
|
||||
|
||||
Mark your tool with `RequiresPolling = true` and specify a `PollAction` (typically `"status"`):
|
||||
|
||||
```csharp
|
||||
[McpForUnityTool(RequiresPolling = true, PollAction = "status")]
|
||||
```
|
||||
|
||||
### The Three Key Ingredients
|
||||
|
||||
1. **Start the work:** Return `new PendingResponse("message", pollIntervalSeconds)` to acknowledge the job has started. The poll interval tells the server how long to wait between checks.
|
||||
|
||||
2. **Implement the poll action:** Create a method (like `Status`) that checks progress and returns `_mcp_status` of `pending`, `complete`, or `error`. **Important:** The middleware calls your `PollAction` string exactly as written—no automatic case conversion—so make sure your `HandleCommand` recognizes it.
|
||||
|
||||
3. **Persist your state:** Use `McpJobStateStore` to save progress to the `Library/` folder. This ensures your tool remembers what it was doing even after a domain reload wipes memory.
|
||||
|
||||
### Complete Example
|
||||
|
||||
```csharp
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.Editor.Tools;
|
||||
|
||||
[McpForUnityTool(
|
||||
"bake_lightmaps",
|
||||
Description = "Simulated async lightmap bake with polling",
|
||||
RequiresPolling = true,
|
||||
PollAction = "status"
|
||||
)]
|
||||
public static class BakeLightmaps
|
||||
{
|
||||
private const string ToolName = "bake_lightmaps";
|
||||
private const float SimulatedDurationSeconds = 5f;
|
||||
|
||||
private static bool s_isRunning;
|
||||
private static double s_lastUpdateTime;
|
||||
|
||||
private class State
|
||||
{
|
||||
public string lastStatus { get; set; }
|
||||
public float progress { get; set; }
|
||||
}
|
||||
|
||||
public static object HandleCommand(JObject @params)
|
||||
{
|
||||
if (s_isRunning)
|
||||
{
|
||||
var existing = McpJobStateStore.LoadState<State>(ToolName) ?? new State { lastStatus = "in_progress", progress = 0f };
|
||||
return new PendingResponse("Bake already running", 0.5, existing);
|
||||
}
|
||||
|
||||
var state = new State { lastStatus = "in_progress", progress = 0f };
|
||||
McpJobStateStore.SaveState(ToolName, state);
|
||||
|
||||
s_isRunning = true;
|
||||
s_lastUpdateTime = EditorApplication.timeSinceStartup;
|
||||
EditorApplication.update += UpdateBake;
|
||||
|
||||
return new PendingResponse("Starting lightmap bake", 0.5, new { state.lastStatus, state.progress });
|
||||
}
|
||||
|
||||
public static object Status(JObject _)
|
||||
{
|
||||
var state = McpJobStateStore.LoadState<State>(ToolName) ?? new State { lastStatus = "unknown", progress = 0f };
|
||||
|
||||
if (state.lastStatus == "completed")
|
||||
{
|
||||
return new { _mcp_status = "complete", message = "Bake finished", data = state };
|
||||
}
|
||||
|
||||
if (state.lastStatus == "error")
|
||||
{
|
||||
return new { _mcp_status = "error", error = "Bake failed", data = state };
|
||||
}
|
||||
|
||||
return new PendingResponse($"Baking... {state.progress:P0}", 0.5, state);
|
||||
}
|
||||
|
||||
private static void UpdateBake()
|
||||
{
|
||||
if (!s_isRunning)
|
||||
{
|
||||
EditorApplication.update -= UpdateBake;
|
||||
return;
|
||||
}
|
||||
|
||||
var now = EditorApplication.timeSinceStartup;
|
||||
var delta = now - s_lastUpdateTime;
|
||||
s_lastUpdateTime = now;
|
||||
|
||||
var state = McpJobStateStore.LoadState<State>(ToolName) ?? new State { lastStatus = "in_progress", progress = 0f };
|
||||
state.progress = Mathf.Clamp01(state.progress + (float)(delta / SimulatedDurationSeconds));
|
||||
|
||||
if (state.progress >= 1f)
|
||||
{
|
||||
state.lastStatus = "completed";
|
||||
s_isRunning = false;
|
||||
EditorApplication.update -= UpdateBake;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.lastStatus = "in_progress";
|
||||
}
|
||||
|
||||
McpJobStateStore.SaveState(ToolName, state);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### How the Polling Protocol Works
|
||||
|
||||
- **`_mcp_status: "pending"`** tells the middleware to keep checking back.
|
||||
- **`_mcp_poll_interval`** (in seconds) controls how long to wait between polls. The server clamps this between 0.1 and 5 seconds to balance responsiveness with performance.
|
||||
- **Null or empty responses** are treated as "still working" and trigger another poll.
|
||||
- **Timeout protection:** After 10 minutes, the server gives up and returns a timeout error along with the last response it received.
|
||||
- **Action routing:** The initial call uses whatever action your tool expects (often implicit). Subsequent polls use your exact `PollAction` string—no automatic snake_case or camelCase conversion—so make sure your `HandleCommand` switch statement handles it correctly.
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
id: multi-instance
|
||||
slug: /guides/multi-instance
|
||||
title: Multi-Instance Routing
|
||||
sidebar_label: Multi-Instance Routing
|
||||
description: Drive several Unity Editors from a single MCP session with set_active_instance and per-call routing.
|
||||
---
|
||||
|
||||
# Multi-Instance Routing
|
||||
|
||||
You can have several Unity Editors open at once and aim a single MCP session at any of them.
|
||||
|
||||
## When this comes up
|
||||
|
||||
- You're refactoring a shared package and need to test the same change in two projects
|
||||
- You're comparing behavior between Unity LTS and Unity 6
|
||||
- You have a runtime project + a tooling project both connected
|
||||
- You're driving a CI fixture project alongside your day-to-day work
|
||||
|
||||
## How instances are identified
|
||||
|
||||
Each connected Unity Editor advertises a stable ID of the form `Name@hash`, where:
|
||||
|
||||
- `Name` is the project's `productName` from Player Settings
|
||||
- `hash` is a stable 8-character hash derived from the project path
|
||||
|
||||
Example: `MyGame@a1b2c3d4`.
|
||||
|
||||
You can also reference an instance by:
|
||||
|
||||
- **Hash prefix** (e.g. `a1b` if it's unambiguous)
|
||||
- **Port number** — stdio transport only
|
||||
|
||||
## Discovering instances
|
||||
|
||||
Read the resource:
|
||||
|
||||
> `mcpforunity://instances`
|
||||
|
||||
It returns the list of currently connected Editors with their `Name@hash`, project path, transport, and port. Most MCP clients expose this as the `unity_instances` resource.
|
||||
|
||||
## Setting the active instance for the session
|
||||
|
||||
```
|
||||
set_active_instance(instance="MyGame@a1b2c3d4")
|
||||
```
|
||||
|
||||
Once set, **every subsequent tool call** in the session routes to that instance until you change it. This is the most common pattern: choose once, then prompt normally.
|
||||
|
||||
You can also use:
|
||||
|
||||
```
|
||||
set_active_instance(instance="a1b") # hash prefix
|
||||
set_active_instance(instance="6401") # port number (stdio only)
|
||||
```
|
||||
|
||||
## Routing a single call without changing the session default
|
||||
|
||||
Pass `unity_instance` on the individual tool call:
|
||||
|
||||
```
|
||||
manage_scene(action="get_hierarchy", unity_instance="MyGame@a1b2c3d4")
|
||||
```
|
||||
|
||||
This is useful for comparing two projects in the same prompt — e.g., "Read the same script from both projects and tell me what differs."
|
||||
|
||||
The server accepts the same value formats as `set_active_instance`: `Name@hash`, hash prefix, or (stdio) port number.
|
||||
|
||||
## What happens with no active instance
|
||||
|
||||
- **One Unity Editor connected** → it's used automatically.
|
||||
- **Multiple Editors connected and no active set** → the server errors with the available instance list. Call `set_active_instance` and retry.
|
||||
|
||||
## HTTP vs stdio differences
|
||||
|
||||
- **HTTP**: instance state is keyed per-session by `client_id`, so two MCP clients can target different Editors at the same time on the same Python server.
|
||||
- **Stdio**: port-number shorthand works because there's a separate Python process per client. HTTP shares one process and uses `Name@hash` exclusively.
|
||||
|
||||
## Related reference
|
||||
|
||||
- [`set_active_instance`](/reference/tools/core/set_active_instance) — full tool reference
|
||||
- [`unity_instances` resource](/reference/resources) — discovery surface
|
||||
@@ -0,0 +1,261 @@
|
||||
# Remote Server API Key Authentication
|
||||
|
||||
When running the MCP for Unity server as a shared remote service, API key authentication ensures that only authorized users can access the server and that each user's Unity sessions are isolated from one another.
|
||||
|
||||
This guide covers how to configure, deploy, and use the feature.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### External Auth Service
|
||||
|
||||
You need an external HTTP endpoint that validates API keys. The server delegates all key validation to this endpoint rather than managing keys itself.
|
||||
|
||||
The endpoint must:
|
||||
|
||||
- Accept `POST` requests with a JSON body: `{"api_key": "<key>"}`
|
||||
- Return a JSON response indicating validity and the associated user identity
|
||||
- Be reachable from the MCP server over the network
|
||||
|
||||
See [Validation Contract](#validation-contract) for the full request/response specification.
|
||||
|
||||
### Transport Mode
|
||||
|
||||
API key authentication is only available when running with HTTP transport (`--transport http`). It has no effect in stdio mode.
|
||||
|
||||
## Server Configuration
|
||||
|
||||
### CLI Arguments
|
||||
|
||||
| Argument | Environment Variable | Default | Description |
|
||||
| -------- | -------------------- | ------- | ----------- |
|
||||
| `--http-remote-hosted` | `UNITY_MCP_HTTP_REMOTE_HOSTED` | `false` | Enable remote-hosted mode. Requires API key auth. |
|
||||
| `--api-key-validation-url URL` | `UNITY_MCP_API_KEY_VALIDATION_URL` | None | External endpoint to validate API keys (required). |
|
||||
| `--api-key-login-url URL` | `UNITY_MCP_API_KEY_LOGIN_URL` | None | URL where users can obtain or manage API keys. |
|
||||
| `--api-key-cache-ttl SECONDS` | `UNITY_MCP_API_KEY_CACHE_TTL` | `300` | How long validated keys are cached (seconds). |
|
||||
| `--api-key-service-token-header HEADER` | `UNITY_MCP_API_KEY_SERVICE_TOKEN_HEADER` | None | Header name for server-to-auth-service authentication. |
|
||||
| `--api-key-service-token TOKEN` | `UNITY_MCP_API_KEY_SERVICE_TOKEN` | None | Token value sent to the auth service for server authentication. |
|
||||
|
||||
Environment variables take effect when the corresponding CLI argument is not provided. For boolean flags, set the env var to `true`, `1`, or `yes`.
|
||||
|
||||
### Startup Validation
|
||||
|
||||
The server validates its configuration at startup:
|
||||
|
||||
- If `--http-remote-hosted` is set but `--api-key-validation-url` is not provided (and the env var is also unset), the server logs an error and exits with code 1.
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
python -m src.main \
|
||||
--transport http \
|
||||
--http-host 0.0.0.0 \
|
||||
--http-port 8080 \
|
||||
--http-remote-hosted \
|
||||
--api-key-validation-url https://auth.example.com/api/validate-key \
|
||||
--api-key-login-url https://app.example.com/api-keys \
|
||||
--api-key-cache-ttl 120
|
||||
```
|
||||
|
||||
Or using environment variables:
|
||||
|
||||
```bash
|
||||
export UNITY_MCP_TRANSPORT=http
|
||||
export UNITY_MCP_HTTP_HOST=0.0.0.0
|
||||
export UNITY_MCP_HTTP_PORT=8080
|
||||
export UNITY_MCP_HTTP_REMOTE_HOSTED=true
|
||||
export UNITY_MCP_API_KEY_VALIDATION_URL=https://auth.example.com/api/validate-key
|
||||
export UNITY_MCP_API_KEY_LOGIN_URL=https://app.example.com/api-keys
|
||||
|
||||
python -m src.main
|
||||
```
|
||||
|
||||
### Service Token (Optional)
|
||||
|
||||
If your auth service requires the MCP server to authenticate itself (server-to-server auth), configure a service token:
|
||||
|
||||
```bash
|
||||
--api-key-service-token-header X-Service-Token \
|
||||
--api-key-service-token "your-server-secret"
|
||||
```
|
||||
|
||||
This adds the specified header to every validation request sent to the auth endpoint.
|
||||
|
||||
We strongly recommend using this feature because it ensures that the entity requesting validation is the MCP server itself, not an imposter.
|
||||
|
||||
## Unity Plugin Setup
|
||||
|
||||
When connecting to a remote-hosted server, Unity users need to provide their API key:
|
||||
|
||||
1. Open the MCP for Unity window in the Unity Editor.
|
||||
2. Select HTTP Remote as the connection mode.
|
||||
3. Enter the API key in the API Key field. The key is stored in `EditorPrefs` (per-machine, not source-controlled).
|
||||
4. Click **Get API Key** to open the login URL in a browser if you need a new key. This fetches the URL from the server's `/api/auth/login-url` endpoint.
|
||||
|
||||
The API key is a one-time entry per machine. It persists across Unity sessions until explicitly cleared.
|
||||
|
||||
## MCP Client Configuration
|
||||
|
||||
When an API key is configured, the Unity plugin's MCP client configurators automatically include the `X-API-Key` header in generated configuration files.
|
||||
|
||||
Example generated config for **Cursor** (`~/.cursor/mcp.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"mcp-for-unity": {
|
||||
"url": "http://remote-server:8080/mcp",
|
||||
"headers": {
|
||||
"X-API-Key": "<your-api-key>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Example for **Claude Code** (CLI):
|
||||
|
||||
```bash
|
||||
claude mcp add --transport http mcp-for-unity http://remote-server:8080/mcp \
|
||||
--header "X-API-Key: <your-api-key>"
|
||||
```
|
||||
|
||||
Similar header injection works for VS Code, Windsurf, Cline, and other supported MCP clients.
|
||||
|
||||
## Behaviour Changes in Remote-Hosted Mode
|
||||
|
||||
Enabling `--http-remote-hosted` changes several server behaviours compared to the default local mode:
|
||||
|
||||
### Authentication Enforcement
|
||||
|
||||
All MCP tool and resource calls require a valid API key. The `X-API-Key` header must be present on every HTTP request to the `/mcp` endpoint. If the key is missing or invalid, the middleware raises a `RuntimeError` that surfaces as an MCP error response.
|
||||
|
||||
### WebSocket Auth Gate
|
||||
|
||||
Unity plugins connecting via WebSocket (`/hub/plugin`) are validated during the handshake:
|
||||
|
||||
| Scenario | WebSocket Close Code | Reason |
|
||||
| -------- | -------------------- | ------ |
|
||||
| No API key header | `4401` | API key required |
|
||||
| Invalid API key | `4403` | Invalid API key |
|
||||
| Auth service unavailable | `1013` | Try again later |
|
||||
| Valid API key | Connection accepted | user_id stored in connection state |
|
||||
|
||||
### Session Isolation
|
||||
|
||||
Each user can only see and interact with their own Unity instances. When User A calls `set_active_instance` or lists instances, they only see Unity editors that connected with User A's API key. User B's sessions are invisible to User A.
|
||||
|
||||
### Auto-Select Disabled
|
||||
|
||||
In local mode, the server automatically selects the sole connected Unity instance. In remote-hosted mode, this auto-selection is disabled. Users must explicitly call `set_active_instance` with a `Name@hash` from the `mcpforunity://instances` resource.
|
||||
|
||||
### CLI Routes Disabled
|
||||
|
||||
The following REST endpoints are disabled in remote-hosted mode to prevent unauthenticated access:
|
||||
|
||||
- `POST /api/command`
|
||||
- `GET /api/instances`
|
||||
- `GET /api/custom-tools`
|
||||
|
||||
### Endpoints Always Available
|
||||
|
||||
These endpoints remain accessible regardless of auth:
|
||||
|
||||
| Endpoint | Method | Purpose |
|
||||
| -------- | ------ | ------- |
|
||||
| `/health` | GET | Health check for load balancers and monitoring |
|
||||
| `/api/auth/login-url` | GET | Returns the login URL for API key management |
|
||||
|
||||
## Validation Contract
|
||||
|
||||
### Request
|
||||
|
||||
```http
|
||||
POST <api-key-validation-url>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"api_key": "<the-api-key>"
|
||||
}
|
||||
```
|
||||
|
||||
If a service token is configured, an additional header is sent:
|
||||
|
||||
```http
|
||||
<service-token-header>: <service-token-value>
|
||||
```
|
||||
|
||||
### Response (Valid Key)
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": true,
|
||||
"user_id": "user-abc-123",
|
||||
"metadata": {}
|
||||
}
|
||||
```
|
||||
|
||||
- `valid` (bool, required): Must be `true`.
|
||||
- `user_id` (string, required): Stable identifier for the user. Used for session isolation.
|
||||
- `metadata` (object, optional): Arbitrary metadata stored alongside the validation result.
|
||||
|
||||
### Response (Invalid Key)
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": false,
|
||||
"error": "API key expired"
|
||||
}
|
||||
```
|
||||
|
||||
- `valid` (bool, required): Must be `false`.
|
||||
- `error` (string, optional): Human-readable reason.
|
||||
|
||||
### Response (HTTP 401)
|
||||
|
||||
A `401` status code is also treated as an invalid key (no body parsing required).
|
||||
|
||||
### Timeouts and Retries
|
||||
|
||||
- Request timeout: 5 seconds
|
||||
- Retries: 1 (with 100ms backoff)
|
||||
- Failure mode: deny by default (treated as invalid on any error)
|
||||
|
||||
Transient failures (5xx, timeouts, network errors) are **not cached**, so subsequent requests will retry the auth service.
|
||||
|
||||
## Error Reference
|
||||
|
||||
| Context | Condition | Response |
|
||||
| ------- | --------- | -------- |
|
||||
| MCP tool/resource | Missing API key (remote-hosted) | `RuntimeError` → MCP `isError: true` |
|
||||
| MCP tool/resource | Invalid API key | `RuntimeError` → MCP `isError: true` |
|
||||
| WebSocket connect | Missing API key | Close `4401` "API key required" |
|
||||
| WebSocket connect | Invalid API key | Close `4403` "Invalid API key" |
|
||||
| WebSocket connect | Auth service down | Close `1013` "Try again later" |
|
||||
| `/api/auth/login-url` | Login URL not configured | HTTP `404` with admin guidance message |
|
||||
| Server startup | Remote-hosted without validation URL | `SystemExit(1)` |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "API key authentication required" error on every tool call
|
||||
|
||||
The server is in remote-hosted mode but no API key is being sent. Ensure the MCP client configuration includes the `X-API-Key` header, or set it in the Unity plugin's connection settings.
|
||||
|
||||
### Server exits immediately with code 1
|
||||
|
||||
The `--http-remote-hosted` flag requires `--api-key-validation-url`. Provide the URL via CLI argument or `UNITY_MCP_API_KEY_VALIDATION_URL` environment variable.
|
||||
|
||||
### WebSocket connection closes with 4401
|
||||
|
||||
The Unity plugin is not sending an API key. Enter the key in the MCP for Unity window's connection settings.
|
||||
|
||||
### WebSocket connection closes with 1013
|
||||
|
||||
The external auth service is unreachable. Check network connectivity between the MCP server and the validation URL. The Unity plugin can retry the connection.
|
||||
|
||||
### User cannot see their Unity instance
|
||||
|
||||
Session isolation is active. The Unity editor and the MCP client must use API keys that resolve to the same `user_id`. Verify that the Unity plugin's WebSocket connection and the MCP client's HTTP requests use the same API key.
|
||||
|
||||
### Stale auth after key rotation
|
||||
|
||||
Validated keys are cached for `--api-key-cache-ttl` seconds (default: 300). After rotating or revoking a key, there is a delay equal to the TTL before the old key stops working. Lower the TTL for faster revocation at the cost of more frequent validation requests.
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: roslyn
|
||||
slug: /guides/roslyn
|
||||
title: Roslyn Script Validation (Advanced)
|
||||
sidebar_label: Roslyn Validation
|
||||
description: Enable strict C# validation that catches undefined namespaces, types, and methods before the script reaches the Unity compiler.
|
||||
---
|
||||
|
||||
# Roslyn Script Validation
|
||||
|
||||
By default, MCP for Unity uses a fast structural validator for scripts the LLM generates. For **strict** validation that catches undefined namespaces, types, and methods at write time — without a full Unity compile — install the optional Roslyn DLLs.
|
||||
|
||||
Most users don't need this. Enable it when:
|
||||
|
||||
- You're letting the LLM write a lot of unsupervised C# and want stricter feedback loops
|
||||
- You're seeing recurring compile errors that survive the structural validator
|
||||
- You're building custom tools that depend on accurate symbol resolution
|
||||
|
||||
## One-click installer (recommended)
|
||||
|
||||
1. Open **Window → MCP for Unity**.
|
||||
2. Scroll to the **Runtime Code Execution (Roslyn)** section in the Scripts / Validation tab.
|
||||
3. Click **Install Roslyn DLLs**.
|
||||
|
||||
The installer downloads the required NuGet packages, places the DLLs in `Assets/Plugins/Roslyn/`, and adds `USE_ROSLYN` to Scripting Define Symbols.
|
||||
|
||||
You can also trigger it from the menu: **Window → MCP for Unity → Install Roslyn DLLs**.
|
||||
|
||||
## Manual install (if the installer isn't available)
|
||||
|
||||
1. Install [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity).
|
||||
2. Open **Window → NuGet Package Manager**.
|
||||
3. Install:
|
||||
- `Microsoft.CodeAnalysis` v5.0
|
||||
- `SQLitePCLRaw.core` v3.0.2
|
||||
- `SQLitePCLRaw.bundle_e_sqlite3` v3.0.2
|
||||
4. Add `USE_ROSLYN` to **Player Settings → Scripting Define Symbols**.
|
||||
5. Restart Unity.
|
||||
|
||||
## Manual DLL install (no NuGetForUnity)
|
||||
|
||||
1. Download `Microsoft.CodeAnalysis.CSharp.dll` and its dependencies from [NuGet.org](https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp/).
|
||||
2. Place DLLs in `Assets/Plugins/Roslyn/`.
|
||||
3. Ensure .NET compatibility settings are correct for your Unity version.
|
||||
4. Add `USE_ROSLYN` to Scripting Define Symbols.
|
||||
5. Restart Unity.
|
||||
|
||||
## Verifying it's active
|
||||
|
||||
After restart, the MCP for Unity status panel shows **Roslyn: enabled** under the Scripts section. The `validate_script` tool now performs full semantic analysis rather than the structural pass.
|
||||
|
||||
## Disabling
|
||||
|
||||
Remove `USE_ROSLYN` from Scripting Define Symbols. The plugin falls back to structural validation; the DLLs can stay in `Assets/Plugins/Roslyn/` or be removed.
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
id: tool-groups
|
||||
slug: /guides/tool-groups
|
||||
title: Tool Groups and manage_tools
|
||||
sidebar_label: Tool Groups
|
||||
description: Per-session visibility for the 47 tools. Activate vfx, animation, ui, testing, etc. only when you need them.
|
||||
---
|
||||
|
||||
# Tool Groups
|
||||
|
||||
MCP for Unity ships 47 tools, but exposing all of them to the LLM at once balloons the prompt and dilutes routing decisions. So tools are sorted into **groups**, and only `core` is enabled by default.
|
||||
|
||||
## The groups
|
||||
|
||||
| Group | Default | Description |
|
||||
|---|---|---|
|
||||
| `core` | enabled | Essential scene, script, asset, and editor tools — always on. |
|
||||
| `animation` | off | Animator control, AnimationClip creation. |
|
||||
| `ui` | off | UI Toolkit — UXML, USS, UIDocument. |
|
||||
| `vfx` | off | VFX Graph, shaders, procedural textures. |
|
||||
| `scripting_ext` | off | ScriptableObject management. |
|
||||
| `testing` | off | Test runner and async test jobs. |
|
||||
| `probuilder` | off | ProBuilder 3D modeling. Requires `com.unity.probuilder` package. |
|
||||
| `profiling` | off | Profiler session control, counters, memory snapshots, Frame Debugger. |
|
||||
| `docs` | off | Unity API reflection and documentation lookup. |
|
||||
|
||||
## Enabling a group
|
||||
|
||||
Use the `manage_tools` meta-tool from your prompt:
|
||||
|
||||
> Activate the `vfx` group so we can author shaders.
|
||||
|
||||
The assistant calls:
|
||||
|
||||
```
|
||||
manage_tools(action="activate", group="vfx")
|
||||
```
|
||||
|
||||
After activation, the group's tools appear in the next tool listing and are usable for the remainder of the session.
|
||||
|
||||
## Listing what's available
|
||||
|
||||
```
|
||||
manage_tools(action="list_groups")
|
||||
```
|
||||
|
||||
Returns every group with its current activation state and tool names.
|
||||
|
||||
## Deactivating
|
||||
|
||||
```
|
||||
manage_tools(action="deactivate", group="vfx")
|
||||
```
|
||||
|
||||
Useful when a group's tools are confusing the assistant — e.g., `manage_shader` and `manage_material` both apply to materials in different ways. Disabling the one you're not using keeps the assistant focused.
|
||||
|
||||
## Other actions
|
||||
|
||||
- `sync` — refreshes visibility from the Unity Editor's per-tool toggle UI. Use after toggling tools in `Window > MCP for Unity > Tools`.
|
||||
- `reset` — restores defaults (only `core` enabled).
|
||||
|
||||
## Why this exists
|
||||
|
||||
Three reasons:
|
||||
|
||||
1. **Prompt economy**: each visible tool adds tokens to every assistant call. Hiding what you're not using is real money saved at scale.
|
||||
2. **Routing clarity**: when the LLM picks between 47 tools versus the 30 core tools, the wrong-tool rate drops measurably.
|
||||
3. **Package hygiene**: tools in `probuilder` only work if `com.unity.probuilder` is installed; hiding them by default avoids confusing errors.
|
||||
|
||||
## Server vs. session state
|
||||
|
||||
- The Unity Editor maintains a per-tool **toggle UI** (`Window > MCP for Unity > Tools`) that controls server-side visibility.
|
||||
- The `manage_tools` meta-tool controls **per-session** visibility — different MCP sessions can see different groups even against the same server.
|
||||
|
||||
`sync` reconciles the two: it pulls the Editor's toggle states into the current session.
|
||||
|
||||
## Related reference
|
||||
|
||||
- [`manage_tools`](/reference/tools/core/manage_tools) — full tool reference
|
||||
- [`tool_groups` resource](/reference/resources) — discoverable group catalog
|
||||
@@ -0,0 +1,183 @@
|
||||
---
|
||||
id: troubleshooting
|
||||
slug: /guides/troubleshooting
|
||||
title: Common Setup Problems
|
||||
sidebar_label: Troubleshooting / FAQ
|
||||
description: Real-world fixes for the issues people actually hit — macOS dyld errors, WSL2 bridging, DLL version conflicts, and per-client FAQs.
|
||||
---
|
||||
|
||||
# Common Setup Problems
|
||||
|
||||
## macOS: Claude CLI fails to start (dyld ICU library not loaded)
|
||||
|
||||
**Symptoms:**
|
||||
- MCP for Unity error: *"Failed to start Claude CLI. dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.71.dylib …"*
|
||||
- Running `claude` in Terminal fails with missing `libicui18n.xx.dylib`.
|
||||
|
||||
**Cause:**
|
||||
Homebrew Node (or the `claude` binary) was linked against an ICU version that's no longer installed; dyld can't find that dylib.
|
||||
|
||||
**Fix options (pick one):**
|
||||
|
||||
**Reinstall Homebrew Node** (relinks to current ICU), then reinstall CLI:
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew reinstall node
|
||||
npm uninstall -g @anthropic-ai/claude-code
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
```
|
||||
|
||||
**Use NVM Node** (avoids Homebrew ICU churn):
|
||||
|
||||
```bash
|
||||
nvm install --lts
|
||||
nvm use --lts
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
# Unity MCP → Claude Code → Choose Claude Location → ~/.nvm/versions/node/<ver>/bin/claude
|
||||
```
|
||||
|
||||
**Use the native installer** (puts `claude` in a stable path):
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
curl -fsSL https://claude.ai/install.sh | bash
|
||||
# Unity MCP → Claude Code → Choose Claude Location → /opt/homebrew/bin/claude or ~/.local/bin/claude
|
||||
```
|
||||
|
||||
**After fixing:** in MCP for Unity (Claude Code section), click **"Choose Claude Location"** and select the working `claude` binary, then **Register** again.
|
||||
|
||||
---
|
||||
|
||||
## WSL2: Connecting Claude Code (Linux) to Unity (Windows)
|
||||
|
||||
If you're running Claude Code from WSL2 and Unity on Windows, the MCP server runs on the Windows side but Claude Code needs to reach it from WSL. Here's how to bridge the two.
|
||||
|
||||
*Contributed by [@aollivier82](https://github.com/CoplayDev/unity-mcp/issues/712).*
|
||||
|
||||
### 1. Install the Unity package
|
||||
|
||||
In Unity Package Manager, add by git URL:
|
||||
|
||||
```text
|
||||
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main
|
||||
```
|
||||
|
||||
In the MCP for Unity settings, change the port to **8090** (or any free port — the default 8080 can conflict with other services like Tailscale).
|
||||
|
||||
### 2. Install uv on Windows
|
||||
|
||||
The MCP server requires `uv`. From an admin PowerShell:
|
||||
|
||||
```powershell
|
||||
irm https://astral.sh/uv/install.ps1 | iex
|
||||
```
|
||||
|
||||
### 3. Set up port forwarding from WSL to Windows
|
||||
|
||||
WSL2 runs in a separate network namespace, so you need to forward the MCP port. From an admin PowerShell:
|
||||
|
||||
```powershell
|
||||
netsh interface portproxy add v4tov4 listenport=8090 listenaddress=0.0.0.0 connectport=8090 connectaddress=127.0.0.1
|
||||
```
|
||||
|
||||
Then add a firewall rule to allow it:
|
||||
|
||||
```powershell
|
||||
New-NetFirewallRule -DisplayName "Unity MCP Server" -Direction Inbound -LocalPort 8090 -Protocol TCP -Action Allow
|
||||
```
|
||||
|
||||
### 4. Find your WSL host IP
|
||||
|
||||
From inside WSL:
|
||||
|
||||
```bash
|
||||
cat /etc/resolv.conf | grep nameserver | awk '{print $2}'
|
||||
```
|
||||
|
||||
This prints the Windows host IP as seen from WSL (e.g. `172.21.48.1`). It's a private address that varies per machine.
|
||||
|
||||
### 5. Add the MCP server to Claude Code
|
||||
|
||||
From WSL, using the IP from step 4:
|
||||
|
||||
```bash
|
||||
claude mcp add --transport http UnityMCP http://<YOUR_WSL_HOST_IP>:8090/mcp
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
claude mcp add --transport http UnityMCP http://172.21.48.1:8090/mcp
|
||||
```
|
||||
|
||||
Note: this uses **HTTP transport**, not stdio — since the server is running on the Windows side.
|
||||
|
||||
### 6. Verify
|
||||
|
||||
Start Unity, then start Claude Code. You should see `UnityMCP` listed as a connected MCP server. Test it by asking Claude to get your scene info.
|
||||
|
||||
**Notes:**
|
||||
- If you restart your machine, the WSL host IP may change. Re-run step 4 and update the MCP config if needed.
|
||||
- The port proxy persists across reboots. To remove it later: `netsh interface portproxy delete v4tov4 listenport=8090 listenaddress=0.0.0.0`
|
||||
- If the connection fails, make sure Unity is running and the MCP server is started (check the MCP for Unity panel).
|
||||
|
||||
---
|
||||
|
||||
## DLL reference mismatch with Unity AI Assistant package
|
||||
|
||||
If you're using **Unity 6.3+** alongside the **Unity AI Assistant** package, you may encounter `System.Collections.Immutable` version conflicts.
|
||||
|
||||
*Reported by [@rkroska](https://github.com/CoplayDev/unity-mcp/issues/557).*
|
||||
|
||||
**Symptoms:**
|
||||
- Compilation errors referencing `System.Collections.Immutable` version mismatches
|
||||
- Errors appear after installing MCP for Unity in a project that has the Unity AI Assistant package
|
||||
|
||||
**Cause:**
|
||||
Unity AI Assistant bundles `System.Collections.Immutable` v10, while MCP for Unity's CodeAnalysis dependency needs v9. Unity's built-in version may be v8. These conflict during assembly resolution.
|
||||
|
||||
**Fix options:**
|
||||
|
||||
- **Option A (recommended):** If you don't need Unity AI Assistant, remove it via Package Manager. Then install `System.Collections.Immutable` v9.0.0 as a DLL in `Assets/Plugins/`.
|
||||
- **Option B:** If you need both packages, install `System.Collections.Immutable` v9.0.0 in `Assets/Plugins/` to satisfy MCP's dependency. The AI Assistant's v10 reference should be forward-compatible.
|
||||
|
||||
**Note:** This is a Unity assembly resolution issue, not specific to MCP for Unity. Unity doesn't have a NuGet-style dependency resolver, so DLL version conflicts must be resolved manually.
|
||||
|
||||
---
|
||||
|
||||
## "No Unity Instances Found"
|
||||
|
||||
:::tip When in doubt, restart your client
|
||||
Clients like Claude Code or JetBrains Rider can get confused if you switch transport modes mid-session. Restart the client so it picks up the new configuration.
|
||||
:::
|
||||
|
||||
If restarting doesn't fix it:
|
||||
- Check the MCP for Unity status panel — does it say `Connected`?
|
||||
- Open `mcpforunity://instances` in your client. If it returns an empty list, the Unity-side bridge isn't running.
|
||||
- Try **Window → MCP for Unity → Restart Server**.
|
||||
|
||||
---
|
||||
|
||||
## FAQ — Claude Code
|
||||
|
||||
**Q: Unity can't find `claude` even though Terminal can.**
|
||||
A: macOS apps launched from Finder / Hub don't inherit your shell PATH. In the MCP for Unity window, click **"Choose Claude Location"** and select the absolute path (e.g., `/opt/homebrew/bin/claude` or `~/.nvm/versions/node/<ver>/bin/claude`).
|
||||
|
||||
**Q: I installed via NVM; where is `claude`?**
|
||||
A: Typically `~/.nvm/versions/node/<ver>/bin/claude`. The MCP for Unity UI also scans NVM versions and you can browse to it via **"Choose Claude Location"**.
|
||||
|
||||
**Q: The Register button says "Claude Not Found".**
|
||||
A: Install the CLI or set the path. Click the orange **[HELP]** link in the MCP for Unity window for step-by-step install instructions, then choose the binary location. See also: [Install or Repair Claude Code CLI](/guides/claude-code-cli).
|
||||
|
||||
## FAQ — VS Code
|
||||
|
||||
**Q: When I first set up and start the MCP for Unity server in VS Code, I get a failed response that says `Canceled: Canceled`.**
|
||||
A: Start a new chat — the bad chat didn't pick up the MCP server configuration.
|
||||
|
||||

|
||||
|
||||
## FAQ — Cursor / Windsurf / VS Code (Windows uv path)
|
||||
|
||||
**Q: My MCP client keeps failing to launch the server even though `uv` is installed.**
|
||||
A: Some Windows machines have multiple `uv.exe` locations. Auto-config sometimes picks a less stable path, causing the launch to fail or auto-rewrite on every restart. Use **"Choose UV Install Location"** in the MCP for Unity window and pin the **WinGet Links shim** path (`%LOCALAPPDATA%\Microsoft\WinGet\Links\uv.exe`) — it's stable across uv upgrades.
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
id: uv-setup
|
||||
slug: /guides/uv-setup
|
||||
title: Install or Repair uv + Python
|
||||
sidebar_label: uv + Python Setup
|
||||
description: Install or repair uv and Python — the runtime MCP for Unity needs to launch the Python server from Cursor, VS Code, Windsurf, Rider, and other uv-based clients.
|
||||
---
|
||||
|
||||
# Install or Repair uv + Python
|
||||
|
||||
The key to configuring MCP with **Cursor, VS Code, Windsurf, and Rider is [`uv`](https://docs.astral.sh/uv/)**.
|
||||
|
||||
- `uv` is a fast Python package manager used to install and run the Unity MCP Server (`mcp-for-unity`).
|
||||
- **How it's used:** your MCP client config points to `command: uvx` with args like `--from mcpforunityserver mcp-for-unity --transport stdio`. The client invokes `uvx` directly to launch the server.
|
||||
- **Why it matters:** if `uv` isn't installed or on PATH, Cursor / Windsurf / VS Code can't start the server. The MCP for Unity window will show **"uv Not Found"** until fixed.
|
||||
- **Detection / override:** the MCP for Unity window auto-detects `uv` in common locations and on PATH. If not found, use **"Choose UV Install Location"** to navigate to your `uv` binary and save the path.
|
||||
|
||||
:::tip When in doubt, restart your client
|
||||
Clients like Claude Code or JetBrains Rider can get confused if you switch from `http` to `stdio` (or vice versa). If they say **"No Unity Instances found"**, restart the client so it picks up the new configuration.
|
||||
:::
|
||||
|
||||
## Requirements
|
||||
|
||||
You need **Python 3.10+** and the **`uv`** package manager.
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
python3 --version # should be 3.10+
|
||||
uv --version # should print a version like "uv 0.x"
|
||||
```
|
||||
|
||||
## Install Python
|
||||
|
||||
**macOS:**
|
||||
|
||||
```bash
|
||||
# Option A: Official installer (recommended)
|
||||
# Download from https://www.python.org/downloads/
|
||||
|
||||
# Option B: Homebrew (3.12 is the latest LTS as of writing; 3.10 also works)
|
||||
brew install python@3.12
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
|
||||
```powershell
|
||||
# Official installer (recommended)
|
||||
# Download from https://www.python.org/downloads/windows/
|
||||
```
|
||||
|
||||
## Install uv
|
||||
|
||||
**macOS / Linux / WSL:**
|
||||
|
||||
```bash
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
# or Homebrew on macOS
|
||||
brew install uv
|
||||
```
|
||||
|
||||
**Windows PowerShell:**
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||
# or
|
||||
winget install --id=astral-sh.uv -e
|
||||
```
|
||||
|
||||
## Common uv locations
|
||||
|
||||
| OS | Path |
|
||||
|---|---|
|
||||
| **macOS** | `/opt/homebrew/bin/uv`, `/usr/local/bin/uv`, `~/.local/bin/uv` |
|
||||
| **Linux** | `/usr/local/bin/uv`, `/usr/bin/uv`, `~/.local/bin/uv` |
|
||||
| **Windows** | `%LOCALAPPDATA%/Programs/Python/Python3xx/Scripts/uv.exe` |
|
||||
|
||||
## MCP for Unity window behavior
|
||||
|
||||
- If `uv` isn't found, the status panel shows a red **"uv Not Found"** with a hint **"Make sure uv is installed! [CLICK]"**.
|
||||
- Use **"Choose UV Install Location"** to browse to the `uv` binary. This saves the path and reconfigures automatically.
|
||||
- On macOS, Unity launched from Finder may not inherit your PATH. Setting the `uv` location here is the easiest fix.
|
||||
|
||||
## Notes and gotchas
|
||||
|
||||
- **macOS GUI apps don't inherit your shell startup files.** PATH may differ from Terminal. Set `uv` via the MCP window to avoid PATH issues.
|
||||
- **Windows vs WSL:** if you installed `uv` inside WSL only, Windows-native Unity can't see it. Install `uv` on Windows, or use the MCP window to point to a Windows `uv.exe`.
|
||||
- **Custom locations:** if you installed `uv` somewhere non-standard, the picker path is stored in `UnityMCP.UvPath` and persists across sessions.
|
||||
|
||||
## What the "Repair Python Env" button does
|
||||
|
||||
- Deletes the server's `.venv` and `.python-version` (if present)
|
||||
- Runs `uv sync` in the Unity MCP Server `src` directory to rebuild a clean environment
|
||||
- Useful after Python upgrades or missing modules
|
||||
|
||||
## Where the Unity MCP Server is installed
|
||||
|
||||
| OS | Path |
|
||||
|---|---|
|
||||
| **macOS** | `~/Library/Application Support/UnityMCP/UnityMcpServer/src` (or `~/Library/AppSupport/UnityMCP/UnityMcpServer/src` via symlink) |
|
||||
| **Windows** | `%USERPROFILE%/AppData/Local/UnityMCP/UnityMcpServer/src` |
|
||||
| **Linux** | `~/.local/share/UnityMCP/UnityMcpServer/src` |
|
||||
|
||||
## Manual repair / run
|
||||
|
||||
```bash
|
||||
cd <UnityMcpServer/src>
|
||||
uv sync
|
||||
uv run server.py
|
||||
```
|
||||
Reference in New Issue
Block a user