unity-mcp/Server/tools/run_tests.py

78 lines
2.3 KiB
Python
Raw Normal View History

"""Tool for executing Unity Test Runner suites."""
from typing import Annotated, Literal, Any
from fastmcp import Context
from pydantic import BaseModel, Field
from models import MCPResponse
from registry import mcp_for_unity_tool
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-06 01:43:36 +08:00
from tools import get_unity_instance_from_context, async_send_with_unity_instance
from unity_connection import async_send_command_with_retry
class RunTestsSummary(BaseModel):
total: int
passed: int
failed: int
skipped: int
durationSeconds: float
resultState: str
class RunTestsTestResult(BaseModel):
name: str
fullName: str
state: str
durationSeconds: float
message: str | None = None
stackTrace: str | None = None
output: str | None = None
class RunTestsResult(BaseModel):
mode: str
summary: RunTestsSummary
results: list[RunTestsTestResult]
class RunTestsResponse(MCPResponse):
data: RunTestsResult | None = None
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-06 01:43:36 +08:00
@mcp_for_unity_tool(
description="Runs Unity tests for the specified mode"
)
async def run_tests(
ctx: Context,
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-06 01:43:36 +08:00
mode: Annotated[Literal["edit", "play"], "Unity test mode to run"] = "edit",
timeout_seconds: Annotated[int | str | None, "Optional timeout in seconds for the Unity test run (string, e.g. '30')"] = None,
) -> dict[str, Any]:
# Get active instance from session state
# Removed session_state import
unity_instance = get_unity_instance_from_context(ctx)
# Coerce timeout defensively (string/float -> int)
def _coerce_int(value, default=None):
if value is None:
return default
try:
if isinstance(value, bool):
return default
if isinstance(value, int):
return int(value)
s = str(value).strip()
if s.lower() in ("", "none", "null"):
return default
return int(float(s))
except Exception:
return default
params: dict[str, Any] = {"mode": mode}
ts = _coerce_int(timeout_seconds)
if ts is not None:
params["timeoutSeconds"] = ts
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-06 01:43:36 +08:00
response = await async_send_with_unity_instance(async_send_command_with_retry, unity_instance, "run_tests", params)
await ctx.info(f'Response {response}')
return RunTestsResponse(**response) if isinstance(response, dict) else response