* 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>
* feat: add JSON property handling for materials; add tests for JSON coercion and end-to-end; update test project manifest and ProjectVersion
* fix(manage_asset): support structured texture blocks case-insensitively; resolve _BaseMap/_MainTex automatically and apply when missing name
* Update MCPForUnity/Editor/Tools/ManageAsset.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* refactor(manage_asset): remove goto; reuse alias resolver for structured texture (supports 'albedo'); tests: self-sufficient texture asset and _BaseColor/_Color guards; python: assert success in invalid JSON case
* chore(manage_asset): remove duplicate return in modify case
* tests: fix mocks/patching for manage_asset/manage_gameobject; make invalid-json case tolerant; ensure prefab modify test patches transport correctly
* ci: allow manual dispatch for Unity EditMode tests (workflow_dispatch)
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* feat: migrate to FastMCP 2.0 (v2.12.5)
- Update pyproject.toml to use fastmcp>=2.12.5 instead of mcp[cli]
- Replace all imports from mcp.server.fastmcp to fastmcp
- Maintain MCP protocol compliance with mcp>=1.16.0
- All 15 files updated with new import statements
- Server and tools registration working with FastMCP 2.0
* chore: bump MCP for Unity to 6.2.2 and widen numeric tool params (asset search/read_resource/run_tests) for better LLM compatibility
* chore: bump installed server_version.txt to 6.2.2 so Unity installer logs correct version
* fix(parameters): apply parameter hardening to read_console, manage_editor, and manage_gameobject
- read_console: accept int|str for count parameter with coercion
- manage_editor: accept bool|str for wait_for_completion with coercion
- manage_gameobject: accept bool|str for all boolean parameters with coercion
- All tools now handle string parameters gracefully and convert to proper types internally
* chore(deps): drop fastmcp, use mcp>=1.18.0; update imports to mcp.server.fastmcp
* chore(deps): re-add fastmcp>=2.12.5, relax mcp to >=1.16.0
Adds fastmcp as explicit dependency for FastMCP 2.0 migration.
Relaxes mcp version constraint to support broader compatibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* test: remove obsolete mcp stubs for FastMCP 2.0 compatibility
Removes stub mcp modules from test files that were conflicting with
the real mcp and fastmcp packages now installed as dependencies.
Adds tests/__init__.py to make tests a proper Python package.
This fixes test collection errors after migrating to FastMCP 2.0.
Test results: 40 passed, 7 xpassed, 5 skipped, 1 failed (pre-existing)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: complete FastMCP 2.0 migration with correct import paths
Updates all remaining files to use `from fastmcp import` instead of
the old `from mcp.server.fastmcp import` path.
Changes:
- server.py: Update FastMCP import
- tools/__init__.py: Update FastMCP import
- resources/__init__.py: Update FastMCP import
- tools/manage_script.py, read_console.py, resource_tools.py: Update imports
- test stubs: Update to stub `fastmcp` instead of `mcp.server.fastmcp`
Addresses PR review feedback about incomplete migration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: harden parameter type handling and resolve PR feedback
Parameter Type Improvements:
- Broaden count in read_console.py to accept int | str
- Broaden build_index in manage_scene.py to accept int | str
- Harden vector parsing in manage_gameobject.py with NaN/Inf checks
- Add whitespace-delimited vector support (e.g., "1 2 3")
- Narrow exception handling from Exception to (ValueError, TypeError)
Test Improvements:
- Harden _load_module in test files with spec/loader validation
- Fix test_manage_gameobject_boolean_and_tag_mapping by mapping tag→search_term
Bug Fixes:
- Fix syntax error in manage_shader.py (remove stray 'x')
Version: Bump to 6.2.3
All tests pass: 41 passed, 5 skipped, 7 xpassed
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Update github-repo-stats.yml
* pytest: make harness MCPForUnity-only; remove UnityMcpBridge paths from tests; route tools.manage_script via unity_connection for reliable monkeypatching; fix ctx usage; all tests green (39 pass, 5 skip, 7 xpass)
* Add missing meta for MaterialMeshInstantiationTests.cs (Assets)
* bridge/tools/manage_script: fix missing unity_connection prefix in validate_script; tests: tidy manage_script_uri unused symbols and arg names
* tests: rename to script_apply_edits_module; extract DummyContext to tests/test_helpers and import; add telemetry stubs in tests to avoid pyproject I/O
* tests: import cleanup and helper extraction; telemetry: prefer plain config and opt-in env override; test stubs and CWD fixes; exclude Bridge from pytest discovery
* chore: remove unintended .wt-origin-main gitlink and ignore folder
* tests: nit fixes (unused-arg stubs, import order, path-normalized ignore hook); telemetry: validate config endpoint; read_console: action optional
* Add development dependencies to pyproject.toml
- Add [project.optional-dependencies] section with dev group
- Include pytest>=8.0.0 and pytest-anyio>=0.6.0
- Add Development Setup section to README-DEV.md with installation and testing instructions
* Revert "Update github-repo-stats.yml"
This reverts commit 8ae595d2f4f2525b0e44ece948883ea37138add4.
* test: improve test clarity and modernize asyncio usage
- Add explanation for 200ms timeout in backpressure test
- Replace manual event loop creation with asyncio.run()
- Add assertion message with actual elapsed time for easier debugging
* refactor: remove duplicate DummyContext definitions across test files
Replace 7 duplicate DummyContext class definitions with imports from tests.test_helpers.
This follows DRY principles and ensures consistency across the test suite.
* chore: remove unused _load function from test_edit_strict_and_warnings.py
Dead code cleanup - function was no longer used after refactoring to dynamic tool registration.
* docs: add comment explaining CWD manipulation in telemetry test
Clarify why os.chdir() is necessary: telemetry.py calls get_package_version()
at module load time, which reads pyproject.toml using a relative path.
Acknowledges the fragility while explaining why it's currently required.
* Copy UnityMcpBridge into a new MCPForUnity folder
This is to close#284
* refactor: rename UnityMcpBridge directory to MCPForUnity in docs
* chore: rename UnityMcpBridge directory to MCPForUnity across workflow files
* chore: rename UnityMcpBridge directory to MCPForUnity across all files
* refactor: update import paths from UnityMcpBridge to MCPForUnity across test files
* fix: update module import paths to use MCPForUnity instead of UnityMcpBridge
* chore: update unity-mcp package path to MCPForUnity directory
* feat: add OneTimeSetUp to initialize CommandRegistry before tests run
Hopefully fix the CI failures
* Apply recent fix to new folder
* Temporarily trigger tests to see if CI works
* Revert "Temporarily trigger tests to see if CI works"
It works!
This reverts commit 8c6eaaad07545cef047769f2c52fe506545a8161.
* CI: streamline Unity licensing (ULF/EBL); drop cache mounts & EBL-in-container; NL suite: clarify T-E/T-J, anchor positions, EOF brace targeting, SHA preconditions
* CI: support both ULF + EBL; validate ULF before -manualLicenseFile; robust readiness wait; use game-ci @v2 actions
* CI: activate EBL via container using UNITY_IMAGE; fix readiness regex grouping
* CI: minimal patch — guard manualLicenseFile by ulf.ok, expand error patterns, keep return-license @v2 for linter
* CI: harden ULF staging (printf+chmod); pass ULF_OK via env; use manual_args array for -manualLicenseFile
* CI: assert EBL activation writes entitlement to host mount; fail fast if missing
* CI: use heredoc in wait step to avoid nested-quote issues; remove redundant EBL artifact copy; drop job-level if and unused UNITY_VERSION
* CI: harden wait step (container status check, broader ready patterns, longer timeout); make license return non-blocking
* CI: wait step — confirm bridge readiness via status JSON (unity_port) + host socket probe
* CI: YAML-safe readiness fallback (grep/sed unity_port + bash TCP probe); workflow_dispatch trigger + ASCII step names
* CI: refine license error pattern to ignore benign LicensingClient channel startup; only match true activation/return failures
* Improve Unity bridge wait logic in CI workflow
- Increase timeout from 600s to 900s for Unity startup
- Add 'bound' to readiness pattern to catch more bridge signals
- Refine error detection to focus only on license failures
- Remove non-license error patterns that could cause false failures
- Improve error reporting with descriptive messages
- Fix regex escaping for unity port parsing
- Fix case sensitivity in sed commands
* Add comprehensive Unity workflow improvements
- Add project warm-up step to pre-import Library before bridge startup
- Expand license mounts to capture full Unity config and local-share directories
- Update bridge container to use expanded directory mounts instead of narrow license paths
- Provide ULF licenses in both legacy and standard local-share paths
- Improve EBL activation to capture complete Unity authentication context
- Update verification logic to check full config directories for entitlements
These changes eliminate cold import delays during bridge startup and provide
Unity with all necessary authentication data, reducing edge cases and improving
overall workflow reliability.
* Refine Unity workflow licensing and permissions
- Make EBL verification conditional on ULF presence to allow ULF-only runs
- Remove read-only mounts from warm-up container for Unity user directories
- Align secrets gate with actual licensing requirements (remove UNITY_SERIAL only)
- Keep return-license action at v2 (latest available version)
These changes prevent workflow failures when EBL has issues but ULF is valid,
allow Unity to write preferences during warm-up, and ensure secrets detection
matches the actual licensing logic used by the workflow steps.
* fix workflow YAML parse
* Normalize NL/T JUnit names and robust summary
* Fix Python import syntax in workflow debug step
* Improve prompt clarity for XML test fragment format
- Add detailed XML format requirements with exact specifications
- Emphasize NO prologue, epilogue, code fences, or extra characters
- Add specific instructions for T-D and T-J tests to write fragments immediately
- Include exact XML template and TESTID requirements
- Should fix T-D and T-J test failures in CI by ensuring proper fragment format
* Fix problematic regex substitution in test name canonicalization
- Replace unsafe regex substitution that could create malformed names
- New approach: preserve correctly formatted names, extract titles safely
- Prevents edge cases where double processing could corrupt test names
- Uses proper em dash (—) separator consistently
- More robust handling of various input formats
* CI: NL/T hardening — enforce filename-derived IDs, robust backfill, single-testcase guard; tighten prompt emissions; disallow Bash
* fix: keep file ID when canonicalizing test names
* CI: move Unity Pro license return to teardown after stopping Unity; keep placeholder at original site
* CI: remove revert helper & baseline snapshot; stop creating scripts dir; prompt: standardize T-B validation to level=standard
* CI: remove mini workflow and obsolete NL prompts; redact email in all Unity log dumps
* NL/T prompt: enforce allowed ops, require per-test fragment emission (incl. failures), add T-F..T-J XML templates
* NL suite: enforce strict NL-4 emission; remove brittle relabeling; keep canonicalization + backfill
* NL/T: minimize transcript; tighten NL-4 console reads; add final errors scan in T-J
* ci: add local validate-nlt-coverage helper
* CI: add staged report fragment promotion step (reports/_staging -> reports/) to support multi-edit reporting
* CI: add staged report fragment promotion step (reports/_staging -> reports/) to support multi-edit reporting
* CI: minor polish and guardrails; keep staged reports promotion and placeholder detection
* read_console: default count=50; normalize types str->list; tolerate legacy payload shapes
* read_console: harden response parsing for legacy shapes (data as list, tuple entries)
* Docs: refresh CI workflow and prompts (remove mini suite refs; per-test emissions, staging, guard)
* CI: move T coverage check after staged promotion; accept _staging as present; dedupe promotion step
* CI: make T retry conditional on explicit coverage probe (not failure()); respect _staging in probe
CodeRabbit fixes:
- Fix Python test assertions to use assert instead of print/return
- Update version consistency: server_version.txt from 3.2.0 to 3.3.0
- Assembly definition references already correctly configured
Greptile style fixes:
- Add missing newlines at end of Unity meta files and source files
- Fix test logic assumptions: use GreaterOrEqual instead of exact counts
- Make test assertions more robust for fuzzy matching algorithms
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>