Skip to content

Conversation

@dsarno
Copy link
Collaborator

@dsarno dsarno commented Nov 5, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for multiple simultaneous Unity Editor instances with automatic discovery and instance selection.
    • New set_active_instance tool to route subsequent commands to a specific Unity instance.
    • New unity_instances resource to list all running Unity Editor instances.
    • Enhanced port discovery with improved fallback and framed protocol handshake.
  • Bug Fixes

    • Port management now automatically finds a new available port if the stored port becomes busy.
    • Improved material color property auto-detection when applying shader colors.
  • Improvements

    • Heartbeat payloads now include project name and Unity version for better tracking.
    • Enhanced logging and telemetry for multi-instance operations.

sakurachan and others added 18 commits October 31, 2025 14:31
- 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>
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 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>
…ting

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
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.
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()
…text

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
- 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
- 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)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 5, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

This PR introduces multi-instance Unity Editor support by replacing the single global connection with a UnityConnectionPool that discovers and manages multiple instances. Port management now falls back to new ports if existing ones become unavailable. Tools and resources are refactored to route commands through a per-session middleware, enabling deterministic targeting of specific instances. New resources and tools expose instance discovery and selection capabilities. Material color property detection now auto-identifies appropriate properties. Status file tracking includes project metadata for improved instance management.

Changes

Cohort / File(s) Summary
Port Discovery & Probing
MCPForUnity/UnityMcpServer~/src/port_discovery.py, Server/port_discovery.py
Enhanced port probing with Unity framed protocol handshake support; added status file scanning for instance discovery; introduced UnityInstanceInfo construction with project name extraction; deduplication by port with freshness handling
Data Models
MCPForUnity/UnityMcpServer~/src/models.py, Server/models.py
Added UnityInstanceInfo class with fields: id, name, path, hash, port, status, last_heartbeat, unity_version; includes to_dict serialization method
Connection Management
MCPForUnity/UnityMcpServer~/src/unity_connection.py, Server/unity_connection.py
Replaced single UnityConnection with UnityConnectionPool for multi-instance support; added instance_id field to UnityConnection; added get_unity_connection_pool() accessor; updated send_command_with_retry and async_send_command_with_retry to accept optional instance_id parameter
Session Middleware
MCPForUnity/UnityMcpServer~/src/unity_instance_middleware.py, Server/unity_instance_middleware.py
Introduced UnityInstanceMiddleware for per-session instance routing; provides thread-safe per-session active instance storage; injects unity_instance into request context state before tool execution
Context-Aware Helpers
MCPForUnity/UnityMcpServer~/src/tools/__init__.py, Server/tools/__init__.py
Added public helpers: get_unity_instance_from_context, send_with_unity_instance, async_send_with_unity_instance, with_unity_instance decorator for transparent instance injection
Resource Routing Updates
MCPForUnity/UnityMcpServer~/src/resources/menu_items.py, Server/resources/menu_items.py
Updated get_menu_items to accept Context parameter and route through async_send_with_unity_instance
Resource Routing Updates
MCPForUnity/UnityMcpServer~/src/resources/tests.py, Server/resources/tests.py
Updated get_tests and get_tests_for_mode to accept Context parameter and route through async_send_with_unity_instance
New Instance Discovery Resource
MCPForUnity/UnityMcpServer~/src/resources/unity_instances.py, Server/resources/unity_instances.py
New resource endpoint to list all running Unity instances with metadata; returns success/instance_count/instances payload
Resource Initialization
MCPForUnity/UnityMcpServer~/src/resources/__init__.py, Server/resources/__init__.py
Added conditional resource template registration for URIs with query parameters; enhanced logging with per-resource counters and summary
Tool Routing Updates (Core)
MCPForUnity/UnityMcpServer~/src/tools/execute_menu_item.py, MCPForUnity/UnityMcpServer~/src/tools/manage_asset.py, MCPForUnity/UnityMcpServer~/src/tools/manage_editor.py, and corresponding Server/tools/* files
Updated tools to retrieve unity_instance from context and route commands through send_with_unity_instance wrapper
Tool Routing Updates (GameObject/Prefabs)
MCPForUnity/UnityMcpServer~/src/tools/manage_gameobject.py, MCPForUnity/UnityMcpServer~/src/tools/manage_prefabs.py, and corresponding Server/tools/* files
Updated manage_gameobject to exclude "get_component" action; manage_prefabs updated with new component_properties parameter and simplified action set
Tool Routing Updates (Scene/Script/Shader)
MCPForUnity/UnityMcpServer~/src/tools/manage_scene.py, MCPForUnity/UnityMcpServer~/src/tools/manage_script.py, MCPForUnity/UnityMcpServer~/src/tools/manage_shader.py, and corresponding Server/tools/* files
Updated to route through unity instance context; simplified parameter descriptions; enhanced logging with instance information
Tool Routing Updates (Tests/Console/Resources)
MCPForUnity/UnityMcpServer~/src/tools/run_tests.py, MCPForUnity/UnityMcpServer~/src/tools/read_console.py, MCPForUnity/UnityMcpServer~/src/tools/resource_tools.py, and corresponding Server/tools/* files
Updated return types and routing; _resolve_project_root now accepts Context parameter for instance-aware resolution
New Active Instance Tool
MCPForUnity/UnityMcpServer~/src/tools/set_active_instance.py, Server/tools/set_active_instance.py
New tool to discover instances and set active instance for session via middleware; supports exact ID (Name@hash) or hash prefix resolution with error handling
Script Editing Tool
MCPForUnity/UnityMcpServer~/src/tools/script_apply_edits.py, Server/tools/script_apply_edits.py
Updated to route all manage_script commands through unity_instance wrapper; enhanced logging with instance context
Debug Tool
MCPForUnity/UnityMcpServer~/src/tools/debug_request_context.py
New tool exposing detailed FastMCP request context information including session state and Unity middleware data
Server Main Entry
MCPForUnity/UnityMcpServer~/src/server.py, Server/server.py
Replaced single UnityConnection with UnityConnectionPool; added UnityInstanceMiddleware registration; added command-line argument support for --default-instance; updated startup telemetry to include instance_count; updated server lifespan to yield pool instead of bridge
Port Management
MCPForUnity/Editor/Helpers/PortManager.cs
Enhanced to fall back to new port discovery when stored port is still busy after waiting; maintains port persistence with fallback logic
Bridge Port Fallback
MCPForUnity/Editor/MCPForUnityBridge.cs
Added fallback to new port via PortManager.GetPortWithFallback() on AddressAlreadyInUse; enhanced heartbeat to include project_name and unity_version; added status file cleanup on stop
Asset Management
MCPForUnity/Editor/Tools/ManageAsset.cs
Added GetMainColorPropertyName() helper for auto-detection of main color property; enhanced property validation with warnings; improved color application logic
Dependencies
MCPForUnity/UnityMcpServer~/src/pyproject.toml, Server/pyproject.toml
Bumped fastmcp dependency from 2.12.5 to 2.13.0
Documentation
README.md
Added sections for set_active_instance tool, unity_instances resource, and usage guide for working with multiple Unity instances
Test Helpers
tests/test_helpers.py
Added DummyContext and _DummyMeta classes for testing with context-based operations
Instance Routing Tests
tests/test_instance_routing_comprehensive.py, tests/test_instance_targeting_resolution.py
New comprehensive test suites for middleware state management, instance routing, and session isolation
Asset Test Updates
tests/test_manage_asset_json_parsing.py, tests/test_manage_asset_param_coercion.py
Updated to use DummyContext; adjusted async patch signatures to accept arbitrary kwargs

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server
    participant Middleware as UnityInstanceMiddleware
    participant Pool as UnityConnectionPool
    participant Instance as UnityInstance

    Client->>Server: Call tool (e.g., manage_asset)
    Server->>Middleware: on_call_tool(ctx)
    Middleware->>Middleware: _get_session_key(ctx)
    Middleware->>Middleware: get_active_instance(session_key)
    Middleware->>Server: inject "unity_instance" into ctx.state
    Server->>Server: get_unity_instance_from_context(ctx)
    Server->>Pool: get_connection(instance_id)
    Pool->>Pool: _resolve_instance_id(instance_id)
    Pool->>Instance: return UnityConnection(instance_id)
    Server->>Instance: send_command_with_retry(cmd, params, instance_id)
    Instance-->>Server: response
    Server-->>Client: result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Critical areas requiring attention:
    • UnityConnectionPool implementation and instance discovery/caching logic — ensures correct multi-instance resolution and state management
    • UnityInstanceMiddleware thread-safety and session key derivation — maintains per-session state integrity across concurrent requests
    • Context-aware routing in all updated tools — verify consistent instance_id threading through async/sync paths and error handling
    • Port fallback logic in PortManager and MCPForUnityBridge — ensure port persistence and fallback coordination work correctly
    • Status file scanning and deduplication in port_discovery.py — verify freshness logic and edge cases (missing files, corrupted JSON)
    • GetMainColorPropertyName() property detection — validate against diverse shader types and ensure fallback behavior
    • Test coverage for race conditions and sequential routing scenarios — verify middleware state isolation under concurrent access

Possibly related PRs

Suggested reviewers

  • justinpbarnett
  • Scriptwonder

Poem

🐰 Whiskers twitch with delight,
Multiple instances, shining bright!
No single connection holds sway,
A pool routes to each host today,
Middleware spins threads with grace—
Context-aware, keeping pace!

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e4b554 and 50c67b1.

📒 Files selected for processing (58)
  • MCPForUnity/Editor/Helpers/PortManager.cs (1 hunks)
  • MCPForUnity/Editor/MCPForUnityBridge.cs (3 hunks)
  • MCPForUnity/Editor/Tools/ManageAsset.cs (5 hunks)
  • MCPForUnity/UnityMcpServer~/src/models.py (2 hunks)
  • MCPForUnity/UnityMcpServer~/src/port_discovery.py (3 hunks)
  • MCPForUnity/UnityMcpServer~/src/pyproject.toml (1 hunks)
  • MCPForUnity/UnityMcpServer~/src/resources/__init__.py (2 hunks)
  • MCPForUnity/UnityMcpServer~/src/resources/menu_items.py (2 hunks)
  • MCPForUnity/UnityMcpServer~/src/resources/tests.py (2 hunks)
  • MCPForUnity/UnityMcpServer~/src/resources/unity_instances.py (1 hunks)
  • MCPForUnity/UnityMcpServer~/src/server.py (6 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/__init__.py (2 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/debug_request_context.py (1 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/execute_menu_item.py (2 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/manage_asset.py (3 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/manage_editor.py (3 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/manage_gameobject.py (3 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/manage_prefabs.py (3 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/manage_scene.py (2 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/manage_script.py (12 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/manage_shader.py (3 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/read_console.py (3 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/resource_tools.py (7 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/run_tests.py (3 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/script_apply_edits.py (8 hunks)
  • MCPForUnity/UnityMcpServer~/src/tools/set_active_instance.py (1 hunks)
  • MCPForUnity/UnityMcpServer~/src/unity_connection.py (8 hunks)
  • MCPForUnity/UnityMcpServer~/src/unity_instance_middleware.py (1 hunks)
  • README.md (3 hunks)
  • Server/models.py (2 hunks)
  • Server/port_discovery.py (3 hunks)
  • Server/pyproject.toml (1 hunks)
  • Server/resources/__init__.py (2 hunks)
  • Server/resources/menu_items.py (2 hunks)
  • Server/resources/tests.py (2 hunks)
  • Server/resources/unity_instances.py (1 hunks)
  • Server/server.py (6 hunks)
  • Server/tools/__init__.py (2 hunks)
  • Server/tools/execute_menu_item.py (2 hunks)
  • Server/tools/manage_asset.py (3 hunks)
  • Server/tools/manage_editor.py (3 hunks)
  • Server/tools/manage_gameobject.py (3 hunks)
  • Server/tools/manage_prefabs.py (3 hunks)
  • Server/tools/manage_scene.py (2 hunks)
  • Server/tools/manage_script.py (12 hunks)
  • Server/tools/manage_shader.py (3 hunks)
  • Server/tools/read_console.py (3 hunks)
  • Server/tools/resource_tools.py (7 hunks)
  • Server/tools/run_tests.py (3 hunks)
  • Server/tools/script_apply_edits.py (8 hunks)
  • Server/tools/set_active_instance.py (1 hunks)
  • Server/unity_connection.py (8 hunks)
  • Server/unity_instance_middleware.py (1 hunks)
  • tests/test_helpers.py (1 hunks)
  • tests/test_instance_routing_comprehensive.py (1 hunks)
  • tests/test_instance_targeting_resolution.py (1 hunks)
  • tests/test_manage_asset_json_parsing.py (9 hunks)
  • tests/test_manage_asset_param_coercion.py (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dsarno dsarno merged commit f667582 into CoplayDev:main Nov 5, 2025
1 check was pending
@dsarno
Copy link
Collaborator Author

dsarno commented Nov 5, 2025

thank you @sakurachan for all the great work on this! and @msanatan for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants