{"owner":"CoplayDev","repo":"unity-mcp","hasSkills":true,"hasMcp":true,"mcpConfig":{"mcpServers":{"unity-mcp":{"command":"npx","args":["-y","@modelcontextprotocol/server-unity-mcp"]}}},"found":["CLAUDE.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## What This Project Is\n\n**MCP for Unity** is a bridge that lets AI assistants (Claude, Cursor, Windsurf, etc.) control the Unity Editor through the Model Context Protocol (MCP). It enables AI-driven game development workflows - creating GameObjects, editing scripts, managing assets, running tests, and more.\n\n## Architecture\n\n```text\nAI Assistant (Claude/Cursor)\n        ↓ MCP Protocol (stdio/HTTP)\nPython Server (Server/src/)\n        ↓ WebSocket + HTTP\nUnity Editor Plugin (MCPForUnity/)\n        ↓ Unity Editor API\nScene, Assets, Scripts\n```\n\n**Two codebases, one system:**\n- `Server/` - Python MCP server using FastMCP\n- `MCPForUnity/` - Unity C# Editor package\n\n### Three Layers on the Python Side\n\nThe Python server has three distinct layers. These are **not** auto-generated from each other:\n\n| Layer | Location | Framework | Purpose |\n|-------|----------|-----------|---------|\n| **MCP Tools** | `Server/src/services/tools/` | FastMCP (`@mcp_for_unity_tool`) | Exposed to AI assistants via MCP protocol |\n| **CLI Commands** | `Server/src/cli/commands/` | Click (`@click.command`) | Terminal interface for developers |\n| **Resources** | `Server/src/services/resources/` | FastMCP (`@mcp_for_unity_resource`) | Read-only state exposed to AI assistants |\n\nMCP tools call Unity via WebSocket (`send_with_unity_instance`). CLI commands call Unity via HTTP (`run_command`). Both route to the same C# `HandleCommand` methods.\n\n### Transport Modes\n\n- **Stdio**: Single-agent only. Separate Python process per client. Legacy TCP bridge to Unity. New connections stomp old ones.\n- **HTTP**: Multi-agent ready. Single shared Python server. WebSocket hub at `/hub/plugin`. Session isolation via `client_id`.\n\n## Code Philosophy\n\n### 1. Domain Symmetry\nPython MCP tools mirror C# Editor tools. Each domain exists in both:\n- `Server/src/services/tools/manage_material.py` ↔ `MCPForUnity/Editor/Tools/ManageMaterial.cs`\n- CLI commands (`Server/src/cli/commands/`) also mirror these but are a separate implementation.\n\n### 2. Minimal Abstraction\nAvoid premature abstraction. Three similar lines of code is better than a helper that's used once. Only abstract when you have 3+ genuine use cases.\n\n### 3. Delete Rather Than Deprecate\nWhen removing functionality, delete it completely. No `_unused` renames, no `// removed` comments, no backwards-compatibility shims for internal code.\n\n### 4. Test Coverage Required\nEvery new feature needs tests. Run them before PRs.\n\n### 5. Keep Tools Focused\nEach MCP tool does one thing well. Resist the urge to add \"convenient\" parameters that bloat the API surface.\n\n### 6. Use Resources for Reading\nKeep them smart and focused rather than \"read everything\" type resources. Resources should be quick and LLM-friendly.\n\n## Key Patterns\n\n### Python MCP Tool Registration\nTools in `Server/src/services/tools/` are auto-discovered. Use the `@mcp_for_unity_tool` decorator:\n```python\nfrom services.registry import mcp_for_unity_tool\n\n@mcp_for_unity_tool(\n    description=\"Does something in Unity.\",\n    group=\"core\",  # core (default), vfx, animation, ui, scripting_ext, testing, probuilder, profiling, docs\n)\nasync def manage_something(\n    ctx: Context,\n    action: Annotated[Literal[\"create\", \"delete\"], \"Action to perform\"],\n) -> dict[str, Any]:\n    unity_instance = await get_unity_instance_from_context(ctx)\n    params = {\"action\": action}\n    response = await send_with_unity_instance(async_send_command_with_retry, unity_instance, \"manage_something\", params)\n    return response\n```\n\nThe `group` parameter controls tool visibility. Only `\"core\"` is enabled by default. Non-core groups (vfx, animation, etc.) start disabled and are toggled via `manage_tools`.\n\n### Python CLI Error Handling\nCLI commands (not MCP tools) use the `@handle_unity_errors` decorator:\n```python\n@handle_unity_errors\nasync def my_command(ctx, ...):\n    result = await call_unity_tool(...)\n```\n\n### C# Tool Registration\nTools are auto-discovered by `CommandRegistry` via reflection. Use the `[McpForUnityTool]` attribute:\n```csharp\n[McpForUnityTool(\"manage_something\", AutoRegister = false, Group = \"core\")]\npublic static class ManageSomething\n{\n    // Sync handler (most tools):\n    public static object HandleCommand(JObject @params)\n    {\n        var p = new ToolParams(@params);\n        // ...\n        return new SuccessResponse(\"Done.\", new { data = result });\n    }\n\n    // OR async handler (for long-running operations like play-test, refresh, batch):\n    public static async Task<object> HandleCommand(JObject @params)\n    {\n        // CommandRegistry detects Task return type automatically\n        await SomeAsyncOperation();\n        return new SuccessResponse(\"Done.\");\n    }\n}\n```\n\nAsync handlers use `EditorApplication.update` polling with `TaskCompletionSource` — see `RefreshUnity.cs` for the canonical pattern.\n\n### C# Parameter Handling\nUse `ToolParams` for consistent parameter validation:\n```csharp\nvar p = new ToolParams(parameters);\nvar pageSize = p.GetInt(\"page_size\", \"pageSize\") ?? 50;\nvar name = p.RequireString(\"name\");\n```\n\n### C# Resources\nResources use `[McpForUnityResource]` and follow the same `HandleCommand` pattern as tools. They provide read-only state to AI assistants.\n\n### Paging Large Results\nAlways page results that could be large (hierarchies, components, search results):\n- Use `page_size` and `cursor` parameters\n- Return `next_cursor` when more results exist\n\n### Composing Tools Internally (C#)\nUse `CommandRegistry.InvokeCommandAsync` to call other tools from within a handler:\n```csharp\nvar result = await CommandRegistry.InvokeCommandAsync(\"read_console\", consoleParams);\n```\n\n### Unity API Compatibility Shims\nWe support a wide Unity version range (2021+ → 6.x → CoreCLR 6.8). When an API is renamed, deprecated, or removed across versions, **don't sprinkle `#if UNITY_x_y_OR_NEWER` at every call site** — add a shim in `MCPForUnity/Runtime/Helpers/Unity*Compat.cs` and route every caller through it.\n\nThe catalog of active shims, the policy for when to add one, what does NOT belong in a shim, and the reflection-cache pattern all live in **`MCPForUnity/Runtime/Helpers/UnityCompatShims.cs`** — the XML doc on that empty marker class is the source of truth and ships inside the UPM package, so end-users can `F12`/Go-to-definition into it. Sources for current deprecations: Unity 6.x upgrade guides and the [CoreCLR 2026 thread](https://discussions.unity.com/t/path-to-coreclr-2026-upgrade-guide/1714279).\n\nWhen you touch a shim or anything else gated by `#if UNITY_*_OR_NEWER`, run `tools/check-unity-versions.sh` to compile-check across the CI matrix locally before committing — the matrix lives in `tools/unity-versions.json`.\n\n## Commands\n\n### Running Tests\n```bash\n# Python (all tests)\ncd Server && uv run pytest tests/ -v\n\n# Python (single test file)\ncd Server && uv run pytest tests/test_manage_material.py -v\n\n# Python (single test by name)\ncd Server && uv run pytest tests/ -k \"test_create_material\" -v\n\n# Unity - open TestProjects/UnityMCPTests in Unity, use Test Runner window\n\n# Local multi-version compile check (parity with CI matrix, see tools/unity-versions.json)\ntools/check-unity-versions.sh           # compile-only across installed Unity Hub editors\ntools/check-unity-versions.sh --full    # full EditMode test run\n```\n\n#### Local headless test harness\nOne command boots a headless Hub-licensed Editor against `TestProjects/UnityMCPTests` and runs the smoke + EditMode + PlayMode legs over the bridge — the same entrypoint CI uses (`.github/workflows/e2e-bridge.yml`):\n\n```bash\npython tools/local_harness.py\n```\n\nKey flags: `--legs smoke,editmode,playmode` (subset to run), `--project-path` (target project, default `TestProjects/UnityMCPTests`), `--reuse` (attach to an already-resident bridge instead of booting one), `--keep-alive` (leave the Editor running after the legs), `--no-warmup` (skip the warm-up import phase).\n\nExit codes: `0` pass, `1` blocking-leg regression, `2` bridge unreachable / setup failure, `3` project does not compile, `4` no Unity license / Hub seat, `5` Editor binary/version not found. Requires a Hub-activated Editor locally (no ULF/serial).\n\n### Local Development\n1. Set **Server Source Override** in MCP for Unity Advanced Settings to your local `Server/` path\n2. Enable **Dev Mode** checkbox to force fresh installs\n3. Use `mcp_source.py` to switch Unity package sources\n4. Test on Windows and Mac if possible, and multiple clients (Claude Desktop and Claude Code are tricky for configuration as of this writing)\n\n### Adding a New Tool\n1. Add Python MCP tool in `Server/src/services/tools/manage_<domain>.py` using `@mcp_for_unity_tool`\n2. Add Python CLI commands in `Server/src/cli/commands/<domain>.py` using Click\n3. Add C# implementation in `MCPForUnity/Editor/Tools/Manage<Domain>.cs` with `[McpForUnityTool]`\n4. Add Python tests in `Server/tests/test_manage_<domain>.py`\n5. Add Unity tests in `TestProjects/UnityMCPTests/Assets/Tests/`\n\n## What Not To Do\n\n- Don't add features without tests\n- Don't create helper functions for one-time operations\n- Don't add error handling for scenarios that can't happen\n- Don't commit to `main` directly - branch off `beta` for PRs\n- Don't add docstrings/comments to code you didn't change\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## What This Project Is\n\n**MCP for Unity** is a bridge that lets AI assistants (Claude, Cursor, Windsurf, etc.) control the Unity Editor through the Model Context Protocol (MCP). It enables AI-driven game development workflows - creating GameObjects, editing scripts, managing assets, running tests, and more.\n\n## Architecture\n\n```text\nAI Assistant (Claude/Cursor)\n        ↓ MCP Protocol (stdio/HTTP)\nPython Server (Server/src/)\n        ↓ WebSocket + HTTP\nUnity Editor Plugin (MCPForUnity/)\n        ↓ Unity Editor API\nScene, Assets, Scripts\n```\n\n**Two codebases, one system:**\n- `Server/` - Python MCP server using FastMCP\n- `MCPForUnity/` - Unity C# Editor package\n\n### Three Layers on the Python Side\n\nThe Python server has three distinct layers. These are **not** auto-generated from each other:\n\n| Layer | Location | Framework | Purpose |\n|-------|----------|-----------|---------|\n| **MCP Tools** | `Server/src/services/tools/` | FastMCP (`@mcp_for_unity_tool`) | Exposed to AI assistants via MCP protocol |\n| **CLI Commands** | `Server/src/cli/commands/` | Click (`@click.command`) | Terminal interface for developers |\n| **Resources** | `Server/src/services/resources/` | FastMCP (`@mcp_for_unity_resource`) | Read-only state exposed to AI assistants |\n\nMCP tools call Unity via WebSocket (`send_with_unity_instance`). CLI commands call Unity via HTTP (`run_command`). Both route to the same C# `HandleCommand` methods.\n\n### Transport Modes\n\n- **Stdio**: Single-agent only. Separate Python process per client. Legacy TCP bridge to Unity. New connections stomp old ones.\n- **HTTP**: Multi-agent ready. Single shared Python server. WebSocket hub at `/hub/plugin`. Session isolation via `client_id`.\n\n## Code Philosophy\n\n### 1. Domain Symmetry\nPython MCP tools mirror C# Editor tools. Each domain exists in both:\n- `Server/src/services/tools/manage_material.py` ↔ `MCPForUnity/Editor/Tools/ManageMaterial.cs`\n- CLI commands (`Server/src/cli/commands/`) also mirror these but are a separate implementation.\n\n### 2. Minimal Abstraction\nAvoid premature abstraction. Three similar lines of code is better than a helper that's used once. Only abstract when you have 3+ genuine use cases.\n\n### 3. Delete Rather Than Deprecate\nWhen removing functionality, delete it completely. No `_unused` renames, no `// removed` comments, no backwards-compatibility shims for internal code.\n\n### 4. Test Coverage Required\nEvery new feature needs tests. Run them before PRs.\n\n### 5. Keep Tools Focused\nEach MCP tool does one thing well. Resist the urge to add \"convenient\" parameters that bloat the API surface.\n\n### 6. Use Resources for Reading\nKeep them smart and focused rather than \"read everything\" type resources. Resources should be quick and LLM-friendly.\n\n## Key Patterns\n\n### Python MCP Tool Registration\nTools in `Server/src/services/tools/` are auto-discovered. Use the `@mcp_for_unity_tool` decorator:\n```python\nfrom services.registry import mcp_for_unity_tool\n\n@mcp_for_unity_tool(\n    description=\"Does something in Unity.\",\n    group=\"core\",  # core (default), vfx, animation, ui, scripting_ext, testing, probuilder, profiling, docs\n)\nasync def manage_something(\n    ctx: Context,\n    action: Annotated[Literal[\"create\", \"delete\"], \"Action to perform\"],\n) -> dict[str, Any]:\n    unity_instance = await get_unity_instance_from_context(ctx)\n    params = {\"action\": action}\n    response = await send_with_unity_instance(async_send_command_with_retry, unity_instance, \"manage_something\", params)\n    return response\n```\n\nThe `group` parameter controls tool visibility. Only `\"core\"` is enabled by default. Non-core groups (vfx, animation, etc.) start disabled and are toggled via `manage_tools`.\n\n### Python CLI Error Handling\nCLI commands (not MCP tools) use the `@handle_unity_errors` decorator:\n```python\n@handle_unity_errors\nasync def my_command(ctx, ...):\n    result = await call_unity_tool(...)\n```\n\n### C# Tool Registration\nTools are auto-discovered by `CommandRegistry` via reflection. Use the `[McpForUnityTool]` attribute:\n```csharp\n[McpForUnityTool(\"manage_something\", AutoRegister = false, Group = \"core\")]\npublic static class ManageSomething\n{\n    // Sync handler (most tools):\n    public static object HandleCommand(JObject @params)\n    {\n        var p = new ToolParams(@params);\n        // ...\n        return new SuccessResponse(\"Done.\", new { data = result });\n    }\n\n    // OR async handler (for long-running operations like play-test, refresh, batch):\n    public static async Task<object> HandleCommand(JObject @params)\n    {\n        // CommandRegistry detects Task return type automatically\n        await SomeAsyncOperation();\n        return new SuccessResponse(\"Done.\");\n    }\n}\n```\n\nAsync handlers use `EditorApplication.update` polling with `TaskCompletionSource` — see `RefreshUnity.cs` for the canonical pattern.\n\n### C# Parameter Handling\nUse `ToolParams` for consistent parameter validation:\n```csharp\nvar p = new ToolParams(parameters);\nvar pageSize = p.GetInt(\"page_size\", \"pageSize\") ?? 50;\nvar name = p.RequireString(\"name\");\n```\n\n### C# Resources\nResources use `[McpForUnityResource]` and follow the same `HandleCommand` pattern as tools. They provide read-only state to AI assistants.\n\n### Paging Large Results\nAlways page results that could be large (hierarchies, components, search results):\n- Use `page_size` and `cursor` parameters\n- Return `next_cursor` when more results exist\n\n### Composing Tools Internally (C#)\nUse `CommandRegistry.InvokeCommandAsync` to call other tools from within a handler:\n```csharp\nvar result = await CommandRegistry.InvokeCommandAsync(\"read_console\", consoleParams);\n```\n\n### Unity API Compatibility Shims\nWe support a wide Unity version range (2021+ → 6.x → CoreCLR 6.8). When an API is renamed, deprecated, or removed across versions, **don't sprinkle `#if UNITY_x_y_OR_NEWER` at every call site** — add a shim in `MCPForUnity/Runtime/Helpers/Unity*Compat.cs` and route every caller through it.\n\nThe catalog of active shims, the policy for when to add one, what does NOT belong in a shim, and the reflection-cache pattern all live in **`MCPForUnity/Runtime/Helpers/UnityCompatShims.cs`** — the XML doc on that empty marker class is the source of truth and ships inside the UPM package, so end-users can `F12`/Go-to-definition into it. Sources for current deprecations: Unity 6.x upgrade guides and the [CoreCLR 2026 thread](https://discussions.unity.com/t/path-to-coreclr-2026-upgrade-guide/1714279).\n\nWhen you touch a shim or anything else gated by `#if UNITY_*_OR_NEWER`, run `tools/check-unity-versions.sh` to compile-check across the CI matrix locally before committing — the matrix lives in `tools/unity-versions.json`.\n\n## Commands\n\n### Running Tests\n```bash\n# Python (all tests)\ncd Server && uv run pytest tests/ -v\n\n# Python (single test file)\ncd Server && uv run pytest tests/test_manage_material.py -v\n\n# Python (single test by name)\ncd Server && uv run pytest tests/ -k \"test_create_material\" -v\n\n# Unity - open TestProjects/UnityMCPTests in Unity, use Test Runner window\n\n# Local multi-version compile check (parity with CI matrix, see tools/unity-versions.json)\ntools/check-unity-versions.sh           # compile-only across installed Unity Hub editors\ntools/check-unity-versions.sh --full    # full EditMode test run\n```\n\n#### Local headless test harness\nOne command boots a headless Hub-licensed Editor against `TestProjects/UnityMCPTests` and runs the smoke + EditMode + PlayMode legs over the bridge — the same entrypoint CI uses (`.github/workflows/e2e-bridge.yml`):\n\n```bash\npython tools/local_harness.py\n```\n\nKey flags: `--legs smoke,editmode,playmode` (subset to run), `--project-path` (target project, default `TestProjects/UnityMCPTests`), `--reuse` (attach to an already-resident bridge instead of booting one), `--keep-alive` (leave the Editor running after the legs), `--no-warmup` (skip the warm-up import phase).\n\nExit codes: `0` pass, `1` blocking-leg regression, `2` bridge unreachable / setup failure, `3` project does not compile, `4` no Unity license / Hub seat, `5` Editor binary/version not found. Requires a Hub-activated Editor locally (no ULF/serial).\n\n### Local Development\n1. Set **Server Source Override** in MCP for Unity Advanced Settings to your local `Server/` path\n2. Enable **Dev Mode** checkbox to force fresh installs\n3. Use `mcp_source.py` to switch Unity package sources\n4. Test on Windows and Mac if possible, and multiple clients (Claude Desktop and Claude Code are tricky for configuration as of this writing)\n\n### Adding a New Tool\n1. Add Python MCP tool in `Server/src/services/tools/manage_<domain>.py` using `@mcp_for_unity_tool`\n2. Add Python CLI commands in `Server/src/cli/commands/<domain>.py` using Click\n3. Add C# implementation in `MCPForUnity/Editor/Tools/Manage<Domain>.cs` with `[McpForUnityTool]`\n4. Add Python tests in `Server/tests/test_manage_<domain>.py`\n5. Add Unity tests in `TestProjects/UnityMCPTests/Assets/Tests/`\n\n## What Not To Do\n\n- Don't add features without tests\n- Don't create helper functions for one-time operations\n- Don't add error handling for scenarios that can't happen\n- Don't commit to `main` directly - branch off `beta` for PRs\n- Don't add docstrings/comments to code you didn't change\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## What This Project Is\n\n**MCP for Unity** is a bridge that lets AI assistants (Claude, Cursor, Windsurf, etc.) control the Unity Editor through the Model Context Protocol (MCP). It enables AI-driven game development workflows - creating GameObjects, editing scripts, managing assets, running tests, and more.\n\n## Architecture\n\n```text\nAI Assistant (Claude/Cursor)\n        ↓ MCP Protocol (stdio/HTTP)\nPython Server (Server/src/)\n        ↓ WebSocket + HTTP\nUnity Editor Plugin (MCPForUnity/)\n        ↓ Unity Editor API\nScene, Assets, Scripts\n```\n\n**Two codebases, one system:**\n- `Server/` - Python MCP server using FastMCP\n- `MCPForUnity/` - Unity C# Editor package\n\n### Three Layers on the Python Side\n\nThe Python server has three distinct layers. These are **not** auto-generated from each other:\n\n| Layer | Location | Framework | Purpose |\n|-------|----------|-----------|---------|\n| **MCP Tools** | `Server/src/services/tools/` | FastMCP (`@mcp_for_unity_tool`) | Exposed to AI assistants via MCP protocol |\n| **CLI Commands** | `Server/src/cli/commands/` | Click (`@click.command`) | Terminal interface for developers |\n| **Resources** | `Server/src/services/resources/` | FastMCP (`@mcp_for_unity_resource`) | Read-only state exposed to AI assistants |\n\nMCP tools call Unity via WebSocket (`send_with_unity_instance`). CLI commands call Unity via HTTP (`run_command`). Both route to the same C# `HandleCommand` methods.\n\n### Transport Modes\n\n- **Stdio**: Single-agent only. Separate Python process per client. Legacy TCP bridge to Unity. New connections stomp old ones.\n- **HTTP**: Multi-agent ready. Single shared Python server. WebSocket hub at `/hub/plugin`. Session isolation via `client_id`.\n\n## Code Philosophy\n\n### 1. Domain Symmetry\nPython MCP tools mirror C# Editor tools. Each domain exists in both:\n- `Server/src/services/tools/manage_material.py` ↔ `MCPForUnity/Editor/Tools/ManageMaterial.cs`\n- CLI commands (`Server/src/cli/commands/`) also mirror these but are a separate implementation.\n\n### 2. Minimal Abstraction\nAvoid premature abstraction. Three similar lines of code is better than a helper that's used once. Only abstract when you have 3+ genuine use cases.\n\n### 3. Delete Rather Than Deprecate\nWhen removing functionality, delete it completely. No `_unused` renames, no `// removed` comments, no backwards-compatibility shims for internal code.\n\n### 4. Test Coverage Required\nEvery new feature needs tests. Run them before PRs.\n\n### 5. Keep Tools Focused\nEach MCP tool does one thing well. Resist the urge to add \"convenient\" parameters that bloat the API surface.\n\n### 6. Use Resources for Reading\nKeep them smart and focused rather than \"read everything\" type resources. Resources should be quick and LLM-friendly.\n\n## Key Patterns\n\n### Python MCP Tool Registration\nTools in `Server/src/services/tools/` are auto-discovered. Use the `@mcp_for_unity_tool` decorator:\n```python\nfrom services.registry import mcp_for_unity_tool\n\n@mcp_for_unity_tool(\n    description=\"Does something in Unity.\",\n    group=\"core\",  # core (default), vfx, animation, ui, scripting_ext, testing, probuilder, profiling, docs\n)\nasync def manage_something(\n    ctx: Context,\n    action: Annotated[Literal[\"create\", \"delete\"], \"Action to perform\"],\n) -> dict[str, Any]:\n    unity_instance = await get_unity_instance_from_context(ctx)\n    params = {\"action\": action}\n    response = await send_with_unity_instance(async_send_command_with_retry, unity_instance, \"manage_something\", params)\n    return response\n```\n\nThe `group` parameter controls tool visibility. Only `\"core\"` is enabled by default. Non-core groups (vfx, animation, etc.) start disabled and are toggled via `manage_tools`.\n\n### Python CLI Error Handling\nCLI commands (not MCP tools) use the `@handle_unity_errors` decorator:\n```python\n@handle_unity_errors\nasync def my_command(ctx, ...):\n    result = await call_unity_tool(...)\n```\n\n### C# Tool Registration\nTools are auto-discovered by `CommandRegistry` via reflection. Use the `[McpForUnityTool]` attribute:\n```csharp\n[McpForUnityTool(\"manage_something\", AutoRegister = false, Group = \"core\")]\npublic static class ManageSomething\n{\n    // Sync handler (most tools):\n    public static object HandleCommand(JObject @params)\n    {\n        var p = new ToolParams(@params);\n        // ...\n        return new SuccessResponse(\"Done.\", new { data = result });\n    }\n\n    // OR async handler (for long-running operations like play-test, refresh, batch):\n    public static async Task<object> HandleCommand(JObject @params)\n    {\n        // CommandRegistry detects Task return type automatically\n        await SomeAsyncOperation();\n        return new SuccessResponse(\"Done.\");\n    }\n}\n```\n\nAsync handlers use `EditorApplication.update` polling with `TaskCompletionSource` — see `RefreshUnity.cs` for the canonical pattern.\n\n### C# Parameter Handling\nUse `ToolParams` for consistent parameter validation:\n```csharp\nvar p = new ToolParams(parameters);\nvar pageSize = p.GetInt(\"page_size\", \"pageSize\") ?? 50;\nvar name = p.RequireString(\"name\");\n```\n\n### C# Resources\nResources use `[McpForUnityResource]` and follow the same `HandleCommand` pattern as tools. They provide read-only state to AI assistants.\n\n### Paging Large Results\nAlways page results that could be large (hierarchies, components, search results):\n- Use `page_size` and `cursor` parameters\n- Return `next_cursor` when more results exist\n\n### Composing Tools Internally (C#)\nUse `CommandRegistry.InvokeCommandAsync` to call other tools from within a handler:\n```csharp\nvar result = await CommandRegistry.InvokeCommandAsync(\"read_console\", consoleParams);\n```\n\n### Unity API Compatibility Shims\nWe support a wide Unity version range (2021+ → 6.x → CoreCLR 6.8). When an API is renamed, deprecated, or removed across versions, **don't sprinkle `#if UNITY_x_y_OR_NEWER` at every call site** — add a shim in `MCPForUnity/Runtime/Helpers/Unity*Compat.cs` and route every caller through it.\n\nThe catalog of active shims, the policy for when to add one, what does NOT belong in a shim, and the reflection-cache pattern all live in **`MCPForUnity/Runtime/Helpers/UnityCompatShims.cs`** — the XML doc on that empty marker class is the source of truth and ships inside the UPM package, so end-users can `F12`/Go-to-definition into it. Sources for current deprecations: Unity 6.x upgrade guides and the [CoreCLR 2026 thread](https://discussions.unity.com/t/path-to-coreclr-2026-upgrade-guide/1714279).\n\nWhen you touch a shim or anything else gated by `#if UNITY_*_OR_NEWER`, run `tools/check-unity-versions.sh` to compile-check across the CI matrix locally before committing — the matrix lives in `tools/unity-versions.json`.\n\n## Commands\n\n### Running Tests\n```bash\n# Python (all tests)\ncd Server && uv run pytest tests/ -v\n\n# Python (single test file)\ncd Server && uv run pytest tests/test_manage_material.py -v\n\n# Python (single test by name)\ncd Server && uv run pytest tests/ -k \"test_create_material\" -v\n\n# Unity - open TestProjects/UnityMCPTests in Unity, use Test Runner window\n\n# Local multi-version compile check (parity with CI matrix, see tools/unity-versions.json)\ntools/check-unity-versions.sh           # compile-only across installed Unity Hub editors\ntools/check-unity-versions.sh --full    # full EditMode test run\n```\n\n#### Local headless test harness\nOne command boots a headless Hub-licensed Editor against `TestProjects/UnityMCPTests` and runs the smoke + EditMode + PlayMode legs over the bridge — the same entrypoint CI uses (`.github/workflows/e2e-bridge.yml`):\n\n```bash\npython tools/local_harness.py\n```\n\nKey flags: `--legs smoke,editmode,playmode` (subset to run), `--project-path` (target project, default `TestProjects/UnityMCPTests`), `--reuse` (attach to an already-resident bridge instead of booting one), `--keep-alive` (leave the Editor running after the legs), `--no-warmup` (skip the warm-up import phase).\n\nExit codes: `0` pass, `1` blocking-leg regression, `2` bridge unreachable / setup failure, `3` project does not compile, `4` no Unity license / Hub seat, `5` Editor binary/version not found. Requires a Hub-activated Editor locally (no ULF/serial).\n\n### Local Development\n1. Set **Server Source Override** in MCP for Unity Advanced Settings to your local `Server/` path\n2. Enable **Dev Mode** checkbox to force fresh installs\n3. Use `mcp_source.py` to switch Unity package sources\n4. Test on Windows and Mac if possible, and multiple clients (Claude Desktop and Claude Code are tricky for configuration as of this writing)\n\n### Adding a New Tool\n1. Add Python MCP tool in `Server/src/services/tools/manage_<domain>.py` using `@mcp_for_unity_tool`\n2. Add Python CLI commands in `Server/src/cli/commands/<domain>.py` using Click\n3. Add C# implementation in `MCPForUnity/Editor/Tools/Manage<Domain>.cs` with `[McpForUnityTool]`\n4. Add Python tests in `Server/tests/test_manage_<domain>.py`\n5. Add Unity tests in `TestProjects/UnityMCPTests/Assets/Tests/`\n\n## What Not To Do\n\n- Don't add features without tests\n- Don't create helper functions for one-time operations\n- Don't add error handling for scenarios that can't happen\n- Don't commit to `main` directly - branch off `beta` for PRs\n- Don't add docstrings/comments to code you didn't change\n","category":"root","tokens":2341}]}