unity-mcp/MCPForUnity
dsarno f667582505
Feature/session based instance routing (#369)
* Add support for multiple Unity instances

* fix port detection

* add missing unity_instance parameter

* add instance params for resources

* Fix CodeRabbit review feedback

- Fix partial framed response handling in port discovery
  Add _recv_exact() helper to ensure complete frame reading
  Prevents healthy Unity instances from being misidentified as offline

- Remove unused default_conn variables in server.py (2 files)
  Fixes Ruff F841 lint error that would block CI/CD

- Preserve sync/async nature of resources in wrapper
  Check if original function is coroutine before wrapping
  Prevents 'dict object is not awaitable' runtime errors

- Fix reconnection to preserve instance_id
  Add instance_id tracking to UnityConnection dataclass
  Reconnection now targets the same Unity instance instead of any available one
  Prevents operations from being applied to wrong project

- Add instance logging to manage_asset for debugging
  Helps troubleshoot multi-instance scenarios

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>

* Fix CodeRabbit feedback: reconnection fallback and annotations safety

Address 3 CodeRabbit review comments:

1. Critical: Guard reconnection fallback to prevent wrong instance routing
   - When instance_id is set but rediscovery fails, now raises ConnectionError
   - Added 'from e' to preserve exception chain for better debugging
   - Prevents silently connecting to different Unity instance
   - Ensures multi-instance routing integrity

2. Minor: Guard __annotations__ access in resource registration
   - Use getattr(func, '__annotations__', {}) instead of direct access
   - Prevents AttributeError for functions without type hints

3. Minor: Remove unused get_type_hints import
   - Clean up unused import in resources/__init__.py

All changes applied to both Server/ and MCPForUnity/ directories.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix instance sorting and logging issues

- Fix sorting logic for instances without heartbeat data: use epoch timestamp instead of current time to properly deprioritize instances with None last_heartbeat
- Use logger.exception() instead of logger.error() in disconnect_all() to include stack traces for better debugging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* update uv.lock to prepare for merging into main

* Restore Python 3.10 lockfiles and package list_unity_instances tool

* Deduplicate Unity instance discovery by port

* Scope status-file reload checks to the active instance

* refactor: implement FastMCP middleware for session-based instance routing

Replaces module-level session_state.py with UnityInstanceMiddleware class
that follows FastMCP best practices. Middleware intercepts all tool calls
via on_call_tool hook and injects active Unity instance into request state.

Key changes:
- Add UnityInstanceMiddleware class with on_call_tool hook
- Tools now use ctx.get_state("unity_instance") instead of direct session_state calls
- Remove unity_instance parameter from all tool schemas to prevent LLM hallucination
- Convert list_unity_instances tool to unity_instances resource (read-only data)
- Update error messages to reference unity://instances resource
- Add set_state/get_state methods to DummyContext test helper
- All 67 tests passing (55 passed, 5 skipped, 7 xpassed)

Architecture benefits:
- Centralized session management in middleware
- Standard FastMCP patterns (middleware + request state)
- Cleaner separation of concerns
- Prevents AI hallucination of invalid instance IDs

* fix: convert resource templates to static resources for discoverability

Convert MCP resources from URI templates with query parameters to static
resources to fix discoverability in MCP clients like Claude Code.

Changes:
- Remove {?force_refresh} from unity://instances
- Remove {?unity_instance} from mcpforunity://menu-items
- Remove {?unity_instance} from mcpforunity://tests
- Keep {mode} path parameter in mcpforunity://tests/{mode} (legitimate)

Root cause: Query parameters {?param} trigger ResourceTemplate registration,
which are listed via resources/templates/list instead of resources/list.
Claude Code's ListMcpResourcesTool only queries resources/list, making
templates undiscoverable.

Solution: Remove optional query parameters from URIs. Instance routing is
handled by middleware/context, and force_refresh was cache control that
doesn't belong in resource identity.

Impact: Resources now discoverable via standard resources/list endpoint and
work with all MCP clients including Claude Code and Cursor.

Requires FastMCP >=2.13.0 for proper RFC 6570 query parameter support.

* feat: improve material properties and sync Server resources

Material Property Improvements (ManageAsset.cs):
- Add GetMainColorPropertyName() helper that auto-detects shader color properties
- Tries _BaseColor (URP), _Color (Standard), _MainColor, _Tint, _TintColor
- Update both named and array color property handling to use auto-detection
- Add warning messages when color properties don't exist on materials
- Split HasProperty check from SetColor to enable error reporting

This fixes the issue where simple color array format [r,g,b,a] defaulted to
_Color property, causing silent failures with URP Lit shader which uses _BaseColor.

Server Resource Sync:
- Sync Server/resources with MCPForUnity/UnityMcpServer~/src/resources
- Remove query parameters from resource URIs for discoverability
- Use session-based instance routing via get_unity_instance_from_context()

* fix: repair instance routing and simplify get_unity_instance_from_context

PROBLEM:
Instance routing was failing - scripts went to wrong Unity instances.
Script1 (intended: ramble) -> went to UnityMCPTests 
Script2 (intended: UnityMCPTests) -> went to ramble 

ROOT CAUSE:
Two incompatible approaches for accessing active instance:
1. Middleware: ctx.set_state() / ctx.get_state() - used by most tools
2. Legacy: ctx.request_context.meta - used by script tools
Script tools were reading from wrong location, middleware had no effect.

FIX:
1. Updated get_unity_instance_from_context() to read from ctx.get_state()
2. Removed legacy request_context.meta code path (98 lines removed)
3. Single source of truth: middleware state only

TESTING:
- Added comprehensive test suite (21 tests) covering all scenarios
- Tests middleware state management, session isolation, race conditions
- Tests reproduce exact 4-script failure scenario
- All 88 tests pass (76 passed + 5 skipped + 7 xpassed)
- Verified fix with live 4-script test: 100% success rate

Files changed:
- Server/tools/__init__.py: Simplified from 75 lines to 15 lines
- MCPForUnity/UnityMcpServer~/src/tools/__init__.py: Same simplification
- tests/test_instance_routing_comprehensive.py: New comprehensive test suite

* refactor: standardize instance extraction and remove dead imports

- Standardize all 18 tools to use get_unity_instance_from_context() helper
  instead of direct ctx.get_state() calls for consistency
- Remove dead session_state imports from with_unity_instance decorator
  that would cause ModuleNotFoundError at runtime
- Update README.md with concise instance routing documentation

* fix: critical timezone and import bugs from code review

- Remove incorrect port safety check that treated reclaimed ports as errors
  (GetPortWithFallback may legitimately return same port if it became available)
- Fix timezone-aware vs naive datetime mixing in unity_connection.py sorting
  (use timestamp() for comparison to avoid TypeError)
- Normalize all datetime comparisons in port_discovery.py to UTC
  (file_mtime and last_heartbeat now consistently timezone-aware)
- Add missing send_with_unity_instance import in Server/tools/manage_script.py
  (was causing NameError at runtime on lines 108 and 488)

All 88 tests pass (76 passed + 5 skipped + 7 xpassed)

---------

Co-authored-by: Sakura <sakurachan@qq.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-05 09:43:36 -08:00
..
Editor Feature/session based instance routing (#369) 2025-11-05 09:43:36 -08:00
Runtime Rename plugin folder to MCPForUnity (#303) 2025-10-03 20:23:28 -04:00
UnityMcpServer~/src Feature/session based instance routing (#369) 2025-11-05 09:43:36 -08:00
Editor.meta Rename plugin folder to MCPForUnity (#303) 2025-10-03 20:23:28 -04:00
README.md Rename plugin folder to MCPForUnity (#303) 2025-10-03 20:23:28 -04:00
README.md.meta Rename plugin folder to MCPForUnity (#303) 2025-10-03 20:23:28 -04:00
Runtime.meta Rename plugin folder to MCPForUnity (#303) 2025-10-03 20:23:28 -04:00
package.json chore: bump version to 6.3.0 2025-10-24 15:52:57 +00:00
package.json.meta Rename plugin folder to MCPForUnity (#303) 2025-10-03 20:23:28 -04:00

README.md

MCP for Unity — Editor Plugin Guide

Use this guide to configure and run MCP for Unity inside the Unity Editor. Installation is covered elsewhere; this document focuses on the Editor window, client configuration, and troubleshooting.

Open the window

  • Unity menu: Window > MCP for Unity

The window has four areas: Server Status, Unity Bridge, MCP Client Configuration, and Script Validation.


Quick start

  1. Open Window > MCP for Unity.
  2. Click “Auto-Setup”.
  3. If prompted:
    • Select the server folder that contains server.py (UnityMcpServer~/src).
    • Install Python and/or uv if missing.
    • For Claude Code, ensure the claude CLI is installed.
  4. Click “Start Bridge” if the Unity Bridge shows “Stopped”.
  5. Use your MCP client (Cursor, VS Code, Windsurf, Claude Code) to connect.

Server Status

  • Status dot and label:
    • Installed / Installed (Embedded) / Not Installed.
  • Mode and ports:
    • Mode: Auto or Standard.
    • Ports: Unity (varies; shown in UI), MCP 6500.
  • Actions:
    • Auto-Setup: Registers/updates your selected MCP client(s), ensures bridge connectivity. Shows “Connected ✓” after success.
    • Rebuild MCP Server: Rebuilds the Python based MCP server
    • Select server folder…: Choose the folder containing server.py.
    • Verify again: Re-checks server presence.
    • If Python isnt detected, use “Open Install Instructions”.

Unity Bridge

  • Shows Running or Stopped with a status dot.
  • Start/Stop Bridge button toggles the Unity bridge process used by MCP clients to talk to Unity.
  • Tip: After Auto-Setup, the bridge may auto-start in Auto mode.

MCP Client Configuration

  • Select Client: Choose your target MCP client (e.g., Cursor, VS Code, Windsurf, Claude Code).
  • Per-client actions:
    • Cursor / VS Code / Windsurf:
      • Auto Configure: Writes/updates your config to launch the server via uv:
        • Command: uv
        • Args: run --directory server.py
      • Manual Setup: Opens a window with a pre-filled JSON snippet to copy/paste into your client config.
      • Choose uv Install Location: If uv isnt on PATH, select the uv binary.
      • A compact “Config:” line shows the resolved config file name once uv/server are detected.
    • Claude Code:
      • Register with Claude Code / Unregister MCP for Unity with Claude Code.
      • If the CLI isnt found, click “Choose Claude Install Location”.
      • The window displays the resolved Claude CLI path when detected.

Notes:

  • The UI shows a status dot and a short status text (e.g., “Configured”, “uv Not Found”, “Claude Not Found”).
  • Use “Auto Configure” for one-click setup; use “Manual Setup” when you prefer to review/copy config.

Script Validation

  • Validation Level options:
    • Basic — Only syntax checks
    • Standard — Syntax + Unity practices
    • Comprehensive — All checks + semantic analysis
    • Strict — Full semantic validation (requires Roslyn)
  • Pick a level based on your projects needs. A description is shown under the dropdown.

Troubleshooting


Tips

  • Enable “Show Debug Logs” in the header for more details in the Console when diagnosing issues.