Commit Graph

108 Commits (beta)

Author SHA1 Message Date
Marcus Sanatan 9b809f5363 Manually bump the server to v7
We're still testing the uvx configuration. By updating the version beforehand, when we test with uvx on v7.0.0 - it should work.

Ideally by the next PR this will be out.
2025-11-05 16:12:54 -04:00
Marcus Sanatan 2619644a3b Update mirror backend with latest code 2025-11-05 16:08:59 -04:00
Marcus Sanatan 2649d9c379
Move Get commands to editor resources + Run Python tests every update (#368)
* Add a function to reload the domain

Closes #357

* feat: restructure server instructions into workflow-focused format

- Reorganized instructions from flat bullet list into categorized workflow sections
- Emphasized critical script management workflow with numbered steps
- Improved readability and scannability for AI agents using the MCP server

It doesn't make sense to repeat the fucnction tools, they're already parsed

* docs: reorder tool list alphabetically in README + add reload_domain tool

* feat: add Unity editor state and project info resources

- Implemented resources for querying active tool, editor state, prefab stage, selection, and open windows
- Added project configuration resources for layers and project metadata
- Organized new resources into Editor and Project namespaces for better structure

* feat: clarify script management workflow in system prompt

- Expanded guidance to include scripts created by any tool, not just manage_script
- Added "etc" to tools examples for better clarity

* refactor: remove reload_domain tool and update script management workflow

- Removed reload_domain tool as Unity automatically recompiles scripts when modified
- Updated script management instructions to rely on editor_state polling and console checking instead of manual domain reload
- Simplified workflow by removing unnecessary manual recompilation step

* Change name of menu items resource as the LLM seems it

* refactor: reorganize tests into src/tests/integration directory

- Moved all test files from root tests/ to MCPForUnity/UnityMcpServer~/src/tests/integration/ for better organization
- Added conftest.py with telemetry and dependency stubs to simplify test setup
- Removed redundant path manipulation and module loading code from individual test files

* feat: expand Unity test workflow triggers

- Run tests on all branches instead of only main
- Add pull request trigger to catch issues before merge
- Maintain path filtering to run only when relevant files change

* chore: add GitHub Actions workflow for Python tests

- Configured automated testing on push and pull requests using pytest
- Set up uv for dependency management and Python 3.10 environment
- Added test results artifact upload for debugging failed runs

* refactor: update import path for fastmcp Context

* docs: update development setup instructions to use uv

- Changed installation commands from pip to uv pip for better dependency management
- Updated test running instructions to use uv run pytest
- Added examples for running integration and unit tests separately

* Formatting [skip ci]

* refactor: optimize CI workflow with path filters and dependency installation

- Added path filters to only trigger tests when Python source or workflow files change
- Split dependency installation into sync and dev install steps for better clarity
- Fixed YAML indentation for improved readability

* Update .github/workflows/python-tests.yml

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: standardize test mode values to match Unity's naming convention

- Changed default mode from "edit" to "EditMode" in C# code
- Updated Python tool to use "EditMode" and "PlayMode" instead of lowercase variants

* refactor: convert test imports to relative imports

- Changed absolute imports to relative imports in integration tests for better package structure
- Removed test packages from pyproject.toml package list

* refactor: use Field with default_factory for mutable default in TagsResponse

* refactor: remove duplicate PrefabStageUtility call

* Update this as well [skip ci]

* Update MCPForUnity/UnityMcpServer~/src/tests/integration/test_script_tools.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: remove pull_request triggers from test workflows [skip ci]

It's already covered by pushes

* refactor: update resource function return types to include MCPResponse union

* refactor: remove manual domain reload tool

- Removed reload_domain tool as Unity handles script recompilation automatically
- Updated documentation to reflect automatic compilation workflow
- Simplified script management workflow instructions in server description

* refactor: add context support to resource handlers

- Updated all resource handlers to accept Context parameter for Unity instance routing
- Replaced direct async_send_command_with_retry calls with async_send_with_unity_instance wrapper
- Added imports for get_unity_instance_from_context and async_send_with_unity_instance helpers

* fix: correct grammar in menu items documentation

* docs: update README with expanded tools and resources documentation

- Added new tools: manage_prefabs, create_script, delete_script, get_sha
- Added new resources: editor state, windows, project info, layers, and tags
- Clarified manage_script as compatibility router with recommendation to use newer edit tools
- Fixed run_test to run_tests for consistency

* refactor: convert unity_instances function to async [skip ci]

- Changed function signature from synchronous to async
- Added await keywords to ctx.info() and ctx.error() calls to properly handle async context methods

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-11-05 16:06:48 -04:00
dsarno f667582505
Feature/session based instance routing (#369)
* Add support for multiple Unity instances

* fix port detection

* add missing unity_instance parameter

* add instance params for resources

* Fix CodeRabbit review feedback

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

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

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

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

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

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

* Fix CodeRabbit feedback: reconnection fallback and annotations safety

Address 3 CodeRabbit review comments:

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

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

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

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

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

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

* Fix instance sorting and logging issues

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

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

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

* update uv.lock to prepare for merging into main

* Restore Python 3.10 lockfiles and package list_unity_instances tool

* Deduplicate Unity instance discovery by port

* Scope status-file reload checks to the active instance

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

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

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

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

* fix: convert resource templates to static resources for discoverability

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

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

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

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

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

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

* feat: improve material properties and sync Server resources

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

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

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

* fix: repair instance routing and simplify get_unity_instance_from_context

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

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

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

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

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

* refactor: standardize instance extraction and remove dead imports

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

* fix: critical timezone and import bugs from code review

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

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

---------

Co-authored-by: Sakura <sakurachan@qq.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-05 09:43:36 -08:00
dsarno e4904ad0d7
Revert "Server: Robust shutdown on stdio detach (signals, stdin/parent monitor, forced exit) (#363)" (#364)
This reverts commit ca01fc7610.
2025-11-01 09:17:04 -07:00
dsarno ca01fc7610
Server: Robust shutdown on stdio detach (signals, stdin/parent monitor, forced exit) (#363)
* Server: robust shutdown on stdio detach (signals, stdin/parent monitor, forced exit)\nTests: move telemetry tests to tests/ and convert to asserts

* Server: simplify _force_exit to os._exit; guard exit timers to avoid duplicates; fix Windows ValueError in parent monitor; tests: add autouse cwd fixture for telemetry to locate pyproject.toml

* Server: add DEBUG logs for transient stdin checks and monitor thread errors

* Mirror shutdown improvements: signal handlers, stdin/parent monitor, guarded exit timers, and os._exit force-exit in UnityMcpServer~ entry points
2025-10-31 21:38:24 -07:00
Marcus Sanatan 040eb6d701
feat: lower minimum Python requirement to 3.10+ (#362)
* feat: lower minimum Python requirement to 3.10+

- Updated Python version requirement from 3.11+ to 3.10+ across all platform detectors
- Added Python 3.10 to MacOS framework search paths for broader compatibility
- Modified version validation logic to accept Python 3.10 or higher
- Updated documentation and error messages to reflect new minimum Python version
- Changed pyproject.toml requires-python field to ">=3.10"
- Updated badges and requirements in README files to show Python 3.10 support

* feat: add Python 3.10 and 3.11 to Windows path detection

- Added Python 3.10 installation path to LocalApplicationData search locations
- Added Python 3.10 and 3.11 paths to ProgramFiles search locations
- Expanded Python version compatibility to support older installations while maintaining support for newer versions

* feat: add Python 3.14 support and update path detection

- Added Python 3.14 installation paths to Windows and macOS platform detectors
- Removed legacy Python 3.9 paths from Windows path detection
- Updated Windows installation recommendations to suggest Python 3.10 or higher
- Added additional Python framework paths (3.10, 3.11) for macOS UV package manager detection
- Extended UV executable path detection to include Python 3.14 locations on both platforms

* Reduce size of README img

* Revert "Reduce size of README img"

This reverts commit 6fb99c7047bdef3610fb94dd3741c71c9e3ffcc1.

* Adjust size in README to maintain quality but be smaller
2025-10-31 15:44:10 -04:00
Marcus Sanatan 0f506e4bee
Copy the MCP server to the top level (#354)
I want to experiment with using `uvx` from this location, and if it manages all the use cases correctly, we won't clone and copy the server code
2025-10-25 00:53:53 -04:00