Skip to content

Feature/session based instance routing#369

Merged
dsarno merged 18 commits into
CoplayDev:mainfrom
dsarno:feature/session-based-instance-routing
Nov 5, 2025
Merged

Feature/session based instance routing#369
dsarno merged 18 commits into
CoplayDev:mainfrom
dsarno:feature/session-based-instance-routing

Conversation

@dsarno

@dsarno dsarno commented Nov 5, 2025

Copy link
Copy Markdown
Collaborator

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

coderabbitai Bot commented Nov 5, 2025

Copy link
Copy Markdown
Contributor

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

dsarno commented Nov 5, 2025

Copy link
Copy Markdown
Collaborator Author

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

@dsarno dsarno deleted the feature/session-based-instance-routing branch November 7, 2025 00:15
choej2303 pushed a commit to choej2303/unity-mcp-ide that referenced this pull request Dec 13, 2025
* 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>
vbucc added a commit to Studio-Pronto/unity-mcp that referenced this pull request Mar 23, 2026
Add ctx.session_id as tier 2 fallback (client_id > session_id > user_id > global)
so multiple Claude Code sessions on the same HTTP server get independent instance
routing. Originally in upstream PR CoplayDev#369, removed in PR CoplayDev#422 for stdio stability.
This restores it below client_id to preserve that fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants