Commit Graph

165 Commits (d6b497f745b136d76e9604307e7da917d4d03171)

Author SHA1 Message Date
dsarno 17c6a36c8d
feat: Add beta server mode with PyPI pre-release support (#640)
* feat: add TestPyPI toggle for pre-release server package testing

- Add UseTestPyPI editor preference key
- Add TestPyPI toggle to Advanced settings UI with tooltip
- Configure uvx to use test.pypi.org when TestPyPI mode enabled
- Skip version pinning in TestPyPI mode to get latest pre-release
- Update ConfigJsonBuilder to handle TestPyPI index URL

* Update .meta file

* fix: Use PyPI pre-release versions instead of TestPyPI for beta server

TestPyPI has polluted packages (broken httpx, mcp, fastapi) that cause
server startup failures. Switch to publishing beta versions directly to
PyPI as pre-releases (e.g., 9.3.0b20260127).

Key changes:
- beta-release.yml: Publish to PyPI instead of TestPyPI, use beta suffix
- Use --prerelease explicit with version specifier (>=0.0.0a0) to only
  get prereleases of our package, not broken dependency prereleases
- Default "Use Beta Server" toggle to true on beta branch
- Rename UI label from "Use TestPyPI" to "Use Beta Server"
- Add UseTestPyPI to EditorPrefsWindow known prefs
- Add search field and refresh button to EditorPrefsWindow

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

* feat: Add beta mode indicator to UI badge and server version logging

- Show "β" suffix on version badge when beta server mode is enabled
- Badge updates dynamically when toggle changes
- Add server version to startup log: "MCP for Unity Server v9.2.0 starting up"
- Add version field to /health endpoint response

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

* refactor: Rename UseTestPyPI to UseBetaServer and fix EditorPrefs margin

- Rename EditorPref key from UseTestPyPI to UseBetaServer for clarity
- Rename all related variables and UXML element names
- Increase bottom margin on EditorPrefs search bar to prevent clipping first entry

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

* refactor: Address code review feedback

- Centralize beta server uvx args in AssetPathUtility.GetBetaServerFromArgs()
  to avoid duplication between HTTP and stdio transports
- Cache server version at startup instead of calling get_package_version()
  on every /health request
- Add robustness to beta version parsing in workflow: strip existing
  pre-release suffix and validate X.Y.Z format before parsing

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

* Prioritize explicit fromUrl override and optimize search filter

- GetBetaServerFromArgs/GetBetaServerFromArgsList now check for explicit
  GitUrlOverride before applying beta server mode, ensuring local dev
  paths and custom URLs are honored
- EditorPrefsWindow search filter uses IndexOf with OrdinalIgnoreCase
  instead of ToLowerInvariant().Contains() for fewer allocations

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

---------

Co-authored-by: Marcus Sanatan <msanatan@gmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-27 11:34:11 -08:00
Shutong Wu feb9e3b6b5
Update the manual commands (#638)
update readme to use the uvx command
2026-01-26 23:32:20 -05:00
dsarno 17eb171e31
feat: replace prefab stage actions with headless modify_contents (#635)
Removes stage-based prefab editing (open_stage, close_stage, save_open_stage)
in favor of headless modify_contents action that uses PrefabUtility.LoadPrefabContents
for reliable automated workflows without UI dialogs.

Changes:
- Remove open_stage, close_stage, save_open_stage actions
- Add modify_contents action for headless prefab editing
- Support targeting objects by name or path (e.g., "Turret/Barrel")
- Support transform, tag, layer, setActive, name, parent, components operations
- Skip saving when no modifications made (avoids unnecessary asset writes)
- Delete PrefabStage.cs resource (no longer needed)
- Update Python tool description to remove "stages" reference
- Consolidate tests from 29 to 14 (covers complex prefabs, reparenting, hierarchy loop guard)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 10:53:44 -08:00
dsarno 9299828eb5
Add meta file for PrefabUtilityHelper (#630) 2026-01-25 20:00:35 -08:00
dsarno ed11e30b47
fix: prefab stage dirty flag, root rename, test fix, and prefab resources (#627)
- Mark prefab stage scene as dirty when manage_components adds/removes/
  modifies components, ensuring save_open_stage correctly detects changes

- When renaming the root GameObject of an open prefab stage, also rename
  the prefab asset file to match, preventing Unity's "file name must
  match" dialog from interrupting automated workflows

- Fix ManagePrefabsCrudTests cleanup order: delete NestedContainer.prefab
  before ChildPrefab.prefab to avoid missing prefab reference errors

- Remove incorrect LogAssert.Expect that expected an error that doesn't
  occur in the test scenario

- Add new prefab MCP resources for inspecting prefabs:
  - mcpforunity://prefab-api: Documentation for prefab resources
  - mcpforunity://prefab/{path}: Get prefab asset info
  - mcpforunity://prefab/{path}/hierarchy: Get full prefab hierarchy

Addresses #97 (Prefab Editor Inspection & Modification Support)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 17:35:01 -08:00
whatevertogo 300a745bf2
feat: Prefab Feature Updates (#611)
* feat: Add prefab read operations (get_info, get_hierarchy, list_prefabs)

- Add get_info: retrieve prefab metadata (GUID, type, components, child count, variant info)
- Add get_hierarchy: get prefab internal structure with pagination support
- Add list_prefabs: search prefabs in project with optional name filtering
- Extract PrefabUtilityHelper class for reusable prefab utility methods
- Update Python tool descriptions and parameter documentation

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

* fix: Use correct API to save prefab stage changes

Replace PrefabUtility.SaveAsPrefabAsset (for creating new prefabs) with
EditorSceneManager.SaveScene to properly save stage modifications.

This fixes the issue where component additions were lost after closing
the prefab stage.

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

* refactor: improve code quality and error handling

- Add pagination constants (DefaultPageSize, MaxPageSize)
- Extract SaveAndRefreshStage helper to reduce duplication
- Change all user-facing messages to English
- Add REQUIRED_PARAMS validation in Python
- Split path parameter into prefab_path and folder_path for clarity
- Improve error handling with specific exception types

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

* feat: Remove list_prefabs action and update related documentation

* feat: Enhance prefab management with detailed parameter descriptions and new unlinking option

* feat: Simplify prefab creation logic and unify logging for asset replacement

* feat: Update SaveStagePrefab method to use SetDirty and SaveAssets for prefab stage saving

* feat: Add PrefabUtilityHelper class with utility methods for prefab asset management

* feat: Refactor action constants and enhance parameter validation in prefab management

* feat: Update ValidateSourceObjectForPrefab method to remove replaceExisting parameter and simplify validation logic

* fix: Fix searchInactive parameter and improve prefab management

- Fix searchInactive not working correctly for child objects
- Improve error message accuracy for object not found
- Use Application.dataPath for reliable directory path resolution

* feat: Add path validation and security checks for prefab operations

* feat: Remove pagination from GetHierarchy method and simplify prefab retrieval

* feat: Remove mode parameter from prefab management functions to simplify usage

* fix: Improve path validation and replace logic in prefab management

* feat: Enhance prefab management by adding nesting depth and parent prefab path retrieval

* fix: resolve Unknown pseudo class last-child USS warnings

Unity UI Toolkit does not support the :last-child pseudo-class. Replace
it with a .section-last class that is applied programmatically to the
last section in each .section-stack container.

Also moves the Configure All Detected Clients button to the bottom
of the Client Configuration section and makes it auto-width.

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

* fix: improve prefab stage save for automated workflows

- Add force parameter to save_open_stage for automated workflows
  where isDirty may not be correctly set
- Use PrefabUtility.SaveAsPrefabAsset for dialog-free saving
- Mark prefab stage scene dirty when modifying GameObjects in prefab mode
- Skip save when no changes and force=false (prevents false dirty flag)

The force parameter ensures reliable saving in CI/automation scenarios
where Unity dirty tracking may be inconsistent with programmatic changes.

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

* Update prefab.py

* refactor: remove unnecessary blank line before create function

* feat: add info and hierarchy commands to prefab CLI for enhanced prefab management

* feat: enhance prefab management with comprehensive CRUD tests and ensure dirty state tracking

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: David Sarno <david@lighthaus.us>
2026-01-25 16:36:29 -08:00
Shutong Wu bb56f78ad3
Token Optimization for VFX (#626)
* Token Optimization for VFX

* Update VFXs

* Small fix based on AI feedback

* Update ManageVFX.cs
2026-01-25 19:34:28 -05:00
dsarno 2eb26b85cf
fix: resolve Unknown pseudo class last-child USS warnings (#624)
Unity UI Toolkit does not support the :last-child pseudo-class. Replace
it with a .section-last class that is applied programmatically to the
last section in each .section-stack container.

Also moves the Configure All Detected Clients button to the bottom
of the Client Configuration section and makes it auto-width.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 11:45:48 -08:00
Shutong Wu e00b2aed4f
[FEATURE] Procedural Texture2D/Sprite Generation (#621)
* Update for Texture2D/Sprite Generation

Given the choice to generate Texture2D based on patterns and color, also introduce pipeline to turn Texture2D direct to Sprite.
Update CLI command to include this too.

* Texture Size Set

Set texture size to 1024X1024 to avoid too large texture set

* Add image input

* Update to release direct error with large tex2d

* Fix for AI advice

* Update on action fetch line
2026-01-24 17:09:07 -05:00
adrd 67dda7f9cc
fix: improve manage_scene screenshot capture (#600)
* fix: improve manage_scene screenshot capture

* fix: address PR review feedback for screenshot capture

- Gate pre-2022 ScreenCapture fallback warning to log only once
- Downgrade warning to Debug.Log to reduce log noise
- Refactor path-building into shared PrepareCaptureResult() helper
- Add conditional logging to catch blocks in BestEffortPrepareGameViewForScreenshot
- Add timeout/failure logging to ScheduleAssetImportWhenFileExists
- Fix grammar in README-DEV.md

* fix(unity): resolve screenshot import callback type + FindObjectsOfType deprecation

* chore: bump version to 9.2.0

* Update ManageScene.cs

* Update ScreenshotUtility.cs

* Update error logging in ManageScene.cs

---------

Co-authored-by: Marcus Sanatan <msanatan@gmail.com>
Co-authored-by: GitHub Actions <actions@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Shutong Wu <51266340+Scriptwonder@users.noreply.github.com>
2026-01-24 17:07:08 -05:00
dsarno cb1a7dd2a1
feat: improve editor window UI + add transport mismatch warning (#613) 2026-01-22 22:48:03 -08:00
GitHub Actions 2c20ddcd10 chore: bump version to 9.2.0 2026-01-23 05:34:19 +00:00
dsarno 30d5bc254e
feat: Add OpenCode (opencode.ai) client configurator (#608)
* feat: Add OpenCode (opencode.ai) client configurator

Add support for the OpenCode CLI client with automatic configuration.

- Create OpenCodeConfigurator implementing IClientConfigurator
- Configure via ~/.config/opencode/opencode.json (XDG standard path)
- Use McpConfigurationHelper for atomic file writes and directory creation
- Support both new config creation and merging with existing config

Co-Authored-By: akshay-kiddopia <akshay@kiddopia.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Address code review feedback for OpenCodeConfigurator

- Add TryLoadConfig() helper to consolidate file read/parse logic
- Handle JsonException separately (log warning, return empty object to overwrite)
- Wrap Configure() in try/catch to prevent crashes, set McpStatus.Error on failure
- Respect XDG_CONFIG_HOME environment variable per XDG Base Directory spec

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

---------

Co-authored-by: akshay-kiddopia <akshay@kiddopia.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 19:46:19 -08:00
GitHub Actions 3ef2d37790 chore: bump version to 9.1.0 2026-01-22 01:00:33 +00:00
Marcus Sanatan 7f44e4b53e
Add CLI (#606)
* feat: Add CLI for Unity MCP server

- Add click-based CLI with 15+ command groups
- Commands: gameobject, component, scene, asset, script, editor, prefab, material, lighting, ui, audio, animation, code
- HTTP transport to communicate with Unity via MCP server
- Output formats: text, json, table
- Configuration via environment variables or CLI options
- Comprehensive usage guide and unit tests

* Update based on AI feedback

* Fixes main.py error

* Update for further error fix

* Update based on AI

* Update script.py

* Update with better coverage and Tool Readme

* Log a message with implicit URI changes

Small update for #542

* Minor fixes (#602)

* Log a message with implicit URI changes

Small update for #542

* Log a message with implicit URI changes

Small update for #542

* Add helper scripts to update forks

* fix: improve HTTP Local URL validation UX and styling specificity

- Rename CSS class from generic "error" to "http-local-url-error" for better specificity
- Rename "invalid-url" class to "http-local-invalid-url" for clarity
- Disable httpServerCommandField when URL is invalid or transport not HTTP Local
- Clear field value and tooltip when showing validation errors
- Ensure field is re-enabled when URL becomes valid

* Docker mcp gateway (#603)

* Log a message with implicit URI changes

Small update for #542

* Update docker container to default to stdio

Replaces #541

* fix: Rider config path and add MCP registry manifest (#604)

- Fix RiderConfigurator to use correct GitHub Copilot config path:
  - Windows: %LOCALAPPDATA%\github-copilot\intellij\mcp.json
  - macOS: ~/Library/Application Support/github-copilot/intellij/mcp.json
  - Linux: ~/.config/github-copilot/intellij/mcp.json
- Add mcp.json for GitHub MCP Registry support:
  - Enables users to install via coplaydev/unity-mcp
  - Uses uvx with mcpforunityserver from PyPI

* Use click.echo instead of print statements

* Standardize whitespace

* Minor tweak in docs

* Use `wait` params

* Unrelated but project scoped tools should be off by default

* Update lock file

* Whitespace cleanup

* Update custom_tool_service.py to skip global registration for any tool name that already exists as a built‑in.

* Avoid silently falling back to the first Unity session when a specific unity_instance was requested but not found.

If a client passes a unity_instance that doesn’t match any session, this code will still route the command to the first available session, which can send commands to the wrong project in multi‑instance environments. Instead, when a unity_instance is provided but no matching session_id is found, return an error (e.g. 400/404 with "Unity instance '' not found") and only default to the first session when no unity_instance was specified.

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Update docs/CLI_USAGE.md

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Updated the CLI command registration to only swallow missing optional modules and to surface real import-time failures, so broken command modules don’t get silently ignored.

* Sorted __all__ alphabetically to satisfy RUF022 in __init__.py.

* Validate --params is a JSON object before merging.

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

---------

Co-authored-by: Shutong Wu <51266340+Scriptwonder@users.noreply.github.com>
Co-authored-by: dsarno <david@lighthaus.us>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-01-21 20:53:13 -04:00
dsarno f54b1cb552
fix: Rider config path and add MCP registry manifest (#604)
- Fix RiderConfigurator to use correct GitHub Copilot config path:
  - Windows: %LOCALAPPDATA%\github-copilot\intellij\mcp.json
  - macOS: ~/Library/Application Support/github-copilot/intellij/mcp.json
  - Linux: ~/.config/github-copilot/intellij/mcp.json
- Add mcp.json for GitHub MCP Registry support:
  - Enables users to install via coplaydev/unity-mcp
  - Uses uvx with mcpforunityserver from PyPI
2026-01-21 13:02:13 -08:00
Marcus Sanatan 810d756be9
Minor fixes (#602)
* Log a message with implicit URI changes

Small update for #542

* Log a message with implicit URI changes

Small update for #542

* Add helper scripts to update forks

* fix: improve HTTP Local URL validation UX and styling specificity

- Rename CSS class from generic "error" to "http-local-url-error" for better specificity
- Rename "invalid-url" class to "http-local-invalid-url" for clarity
- Disable httpServerCommandField when URL is invalid or transport not HTTP Local
- Clear field value and tooltip when showing validation errors
- Ensure field is re-enabled when URL becomes valid
2026-01-21 14:41:16 -04:00
Alex Hvesuk 1cc582636d
Add localhost setup feedback and remove UI issues (#587) 2026-01-21 14:14:13 -04:00
Vladimir Ivanov abd596ff4a
use localhost to ping server if server binds to 0.0.0.0 (#542)
Co-authored-by: vladimir.ivanov <terakriper@gmail.com>
2026-01-21 13:17:52 -04:00
Marcus Sanatan b69ee80da9
Project scoped tools (#596)
* feat: Add project-scoped tools flag to control custom tool registration behavior

Add `--project-scoped-tools` CLI flag and `UNITY_MCP_PROJECT_SCOPED_TOOLS` environment variable to control whether custom tools are registered globally or scoped to specific Unity projects.

Closes #416

* Add .meta file

* feat: Add project-scoped tools toggle for local HTTP transport

Add UI toggle in Connection section to control project-scoped tools flag when using HTTP Local transport. The toggle:
- Defaults to enabled (true)
- Persists state in EditorPrefs
- Only displays when HTTP Local transport is selected
- Automatically appends `--project-scoped-tools` flag to uvx server command
- Updates manual config display when toggled

* Update Server/src/services/custom_tool_service.py

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Pass project_scoped_tools flag directly without environment variable conversion

Remove unnecessary environment variable conversion for project_scoped_tools flag.

* fix: Improve error handling and logging in global custom tool registration

Split exception handling to distinguish between expected RuntimeError (service not initialized) and unexpected errors.

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2026-01-21 13:07:52 -04:00
dsarno 8eb684ddc2
fix: comprehensive performance optimizations, claude code config, and stability improvements (issue #577) (#595)
* fix: reduce per-frame GC allocations causing editor hitches (issue #577)

Eliminate memory allocations that occurred every frame, which triggered
garbage collection spikes (~28ms) approximately every second.

Changes:
- EditorStateCache: Skip BuildSnapshot() entirely when state unchanged
  (check BEFORE building). Increased poll interval from 0.25s to 1.0s.
  Cache DeepClone() results to avoid allocations on GetSnapshot().

- TransportCommandDispatcher: Early exit before lock/list allocation
  when Pending.Count == 0, eliminating per-frame allocations when idle.

- StdioBridgeHost: Same early exit pattern for commandQueue.

- MCPForUnityEditorWindow: Throttle OnEditorUpdate to 2-second intervals
  instead of every frame, preventing expensive socket checks 60+/sec.

Fixes GitHub issue #577: High performance impact even when MCP server is off

* fix: prevent multiple domain reloads when calling refresh_unity (issue #577)

Root Cause:
- send_command() had a hardcoded retry loop (min 6 attempts) on connection errors
- Each retry resent the refresh_unity command, causing Unity to reload 6 times
- retry_on_reload=False only controlled reload-state retries, not connection retries

The Fix:
1. Unity C# (MCPForUnity/Editor):
   - Added --reinstall flag to uvx commands in dev mode
   - Ensures local development changes are picked up by uvx/Claude Code
   - Applies to all client configurators (Claude Code, Codex, etc.)

2. Python Server (Server/src):
   - Added max_attempts parameter to send_command()
   - Pass max_attempts=0 when retry_on_reload=False
   - Fixed type handling in refresh_unity.py (handle MCPResponse objects)
   - Added timeout to connection error recovery conditions
   - Recovery logic now returns success instead of error to prevent client retries

Changes:
- MCPForUnity/Editor: Added --reinstall to dev mode uvx commands
- Server/refresh_unity.py: Fixed type handling, improved error recovery
- Server/unity_connection.py: Added max_attempts param, disable retries when retry_on_reload=False

Result: refresh_unity with compile=request now triggers only 1 domain reload instead of 6

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

* fix: UI and server stability improvements

Unity Editor (C#):
- Fix "Resuming..." stuck state when manually clicking End Session
  Clear ResumeStdioAfterReload and ResumeHttpAfterReload flags in
  OnConnectionToggleClicked and EndOrphanedSessionAsync to prevent
  UI from getting stuck showing "Resuming..." with disabled button
- Remove unsupported --reinstall flag from all uvx command builders
  uvx does not support --reinstall and shows warning when used
  Use --no-cache --refresh instead for dev mode cache busting

Python Server:
- Add "aborted" to connection error patterns in refresh_unity
  Handle WinError 10053 (connection aborted) gracefully during
  Unity domain reload, treating it as expected behavior
- Add WindowsSafeRotatingFileHandler to suppress log rotation errors
  Windows file locking prevents log rotation when file is open by
  another process; catch PermissionError to avoid noisy stack traces
- Fix packaging: add py-modules = ["main"] to pyproject.toml
  setuptools.packages.find only discovers packages (directories with
  __init__.py), must explicitly list standalone module files

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

* docs: improve refresh_unity connection loss handling documentation

Add detailed comments and logging to clarify why connection loss during
compile is treated as success (expected domain reload behavior, not failure).
This addresses PR feedback about potentially masking real connection errors.

The logic is intentional and correct:
- Connection loss only treated as success when compile='request'
- Domain reload causing disconnect is expected Unity behavior
- Subsequent wait_for_ready loop validates Unity becomes ready
- Prevents multiple domain reload loops (issue #577)

Added logging for observability:
- Info log when expected disconnect detected
- Warning log for non-recoverable errors

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

* fix: add missing logger import in refresh_unity

Missing logger import causes NameError at runtime when connection
loss handling paths are triggered (lines 82 and 91).

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

* fix: address code review feedback on thread safety and semantics

Addresses four issues raised in code review:

1. EditorStateCache.GetSnapshot() - Remove shared cached clone
   - Revert to always returning fresh DeepClone() to prevent mutation bugs
   - Main GC optimization remains: state-change detection prevents
     unnecessary _cached rebuilds (the expensive operation)
   - Removed _cachedClone and _cachedCloneSequence fields

2. refresh_unity.py - Fix blocking reason terminology mismatch
   - Changed "asset_refresh" to "asset_import" to match activityPhase
     values from EditorStateCache.cs
   - Ensures asset import is correctly detected as blocking state

3. TransportCommandDispatcher - Fix unsynchronized Count access
   - Moved Pending.Count check inside PendingLock
   - Prevents data races and InvalidOperationException from concurrent
     dictionary access

4. StdioBridgeHost - Fix unsynchronized Count access
   - Moved commandQueue.Count check inside lockObj
   - Ensures all collection access is properly serialized

All changes maintain the GC allocation optimizations while fixing
thread safety violations and semantic contract changes.

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

* fix: address code review feedback on thread safety and timeout handling

- refresh_unity.py: Track readiness explicitly and return failure on timeout
  instead of silently returning success when wait loop exits without confirming
  ready_for_tools=true

- McpClientConfiguratorBase.cs: Add thread safety guard for Configure() call
  in CheckStatusWithProjectDir(). Changed default attemptAutoRewrite to false
  and added runtime check to prevent calling Configure() from background threads.

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

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-20 18:11:25 -08:00
dsarno aaf6308b33
fix: Add special handling for UIDocument serialization to prevent infinite loops (#586)
* fix: search inactive objects when setActive=true in modify

When trying to activate an inactive GameObject via manage_gameobject modify with setActive=true, the lookup would fail because inactive objects were not included in the search by default.

Now automatically sets searchInactive=true when setActive=true is specified, allowing inactive objects to be found and activated.

* fix: Add special handling for UIDocument serialization to prevent infinite loops (#585)

UIDocument.rootVisualElement contains circular parent/child references that
can cause infinite serialization loops. This adds special handling similar to
Transform and Camera components.

The fix:
- Safely serializes panelSettings, visualTreeAsset, sortingOrder, enabled, parentUI
- Explicitly skips rootVisualElement to prevent circular reference issues
- Includes a note explaining why rootVisualElement is skipped

Tested on Unity 2021.3 and Unity 6.3.

* refactor: Extract SerializeAssetReference helper and align UIDocument structure

- Add SerializeAssetReference() helper for consistent asset reference serialization
- UIDocument now uses same return structure as Camera (typeName, instanceID, properties)
- Reduces code duplication in special-case handlers
- Enhanced test coverage to verify structure matches Camera pattern

* fix: Handle UIDocument subclasses and add negative assertion for rootVisualElement

Address code review feedback:
- Add IsOrDerivedFrom() helper to detect UIDocument and any subclasses by walking
  the base-type chain, ensuring derived types also get special-case handling
- Add negative assertion verifying rootVisualElement is NOT in serialized output
2026-01-19 08:21:36 -08:00
dsarno f4e441c563
fix: Filter EditorApplication.isCompiling false positives in Play mode (#582)
EditorApplication.isCompiling can return true in Play mode even when Unity
is not actually compiling, causing MCP tools to incorrectly return
"busy/compiling" errors.

This adds a GetActualIsCompiling() helper that double-checks with
CompilationPipeline.isCompiling via reflection when in Play mode to filter
out these false positives.

Fixes #549
2026-01-19 07:31:53 -08:00
dsarno 8252e6debc
fix: search inactive objects when setActive=true in modify (#581)
When trying to activate an inactive GameObject via manage_gameobject modify with setActive=true, the lookup would fail because inactive objects were not included in the search by default.

Now automatically sets searchInactive=true when setActive=true is specified, allowing inactive objects to be found and activated.
2026-01-19 07:04:54 -08:00
cyanxwh e617753b4e
fix: Add Prefab Stage support for GameObject lookup (#573)
* Enhance Prefab Stage support in GameObject lookup and scene management

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

* fix: unify path matching and restore fast path lookup

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 06:46:29 -08:00
GitHub Actions 2cdc386b55 chore: bump version to 9.0.8 2026-01-19 14:07:05 +00:00
whatevertogo 322a3d1846
fix: resolve Claude Code HTTP Remote UV path override not being detected in System Requirements .#550
* fix: resolve UV path override not being detected in System Requirements

Fixes #538

The System Requirements panel showed "UV Package Manager: Not Found" even
when a valid UV path override was configured in Advanced Settings.

Root cause: PlatformDetectorBase.DetectUv() only searched PATH with bare
command names ("uvx", "uv") and never consulted PathResolverService which
respects the user's override setting.

Changes:
- Refactor DetectUv() to use PathResolverService.GetUvxPath() which checks
  override path first, then system PATH, then falls back to "uvx"
- Add TryValidateUvExecutable() to verify executables by running --version
  instead of just checking File.Exists
- Prioritize PATH environment variable in EnumerateUvxCandidates() for
  better compatibility with official uv install scripts
- Fix process output read order (ReadToEnd before WaitForExit) to prevent
  potential deadlocks

Co-Authored-By: ChatGLM 4.7 <noreply@zhipuai.com>

* fix: improve uv/uvx detection robustness on macOS and Linux

- Read both stdout and stderr when validating uv/uvx executables
- Respect WaitForExit timeout return value instead of ignoring it
- Fix version parsing to handle extra tokens like "(Homebrew 2025-01-01)"
- Resolve bare commands ("uv"/"uvx") to absolute paths after validation
- Rename FindExecutableInPath to FindUvxExecutableInPath for clarity

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

* refactor: unify process execution with ExecPath.TryRun and add Windows PATH augmentation

Replace direct Process.Start calls with ExecPath.TryRun across all platform detectors.
This change:
- Fixes potential deadlocks by using async output reading
- Adds proper timeout handling with process termination
- Removes redundant fallback logic and simplifies version parsing
- Adds Windows PATH augmentation with common uv, npm, and Python installation paths

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

* fix: improve version parsing to handle both spaces and parentheses

The version extraction logic now properly handles outputs like:
- "uvx 0.9.18" -> "0.9.18"
- "uvx 0.9.18 (hash date)" -> "0.9.18"
- "uvx 0.9.18 extra info" -> "0.9.18"

Uses Math.Min to find the first occurrence of either space or parenthesis.

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

* refactor: improve platform detectors with absolute path resolution

- Add absolute path resolution in TryValidatePython and TryValidateUvWithPath for better UI display
- Fix BuildAugmentedPath to avoid PATH duplication
- Add comprehensive comments for version parsing logic
- Ensure cross-platform consistency across all three detectors
- Fix override path validation logic with clear state handling
- Fix platform detector path resolution and Python version detection
- Use UserProfile consistently in GetClaudeCliPath instead of Personal
- All platforms now use protected BuildAugmentedPath method

This change improves user experience by displaying full paths in the UI
while maintaining robust fallback behavior if path resolution fails.

Co-Authored-By: GLM4.7 <noreply@zhipuai.com>

* fix: improve error handling in PathResolverService by logging exceptions

* Remove .meta files added after fork and update .gitignore

* Update .gitignore

* save .meta

* refactor: unify uv/uvx naming and path detection across platforms

- Rename TryValidateUvExecutable -> TryValidateUvxExecutable for consistency
- Add cross-platform FindInPath() helper in ExecPath.cs
- Remove platform-specific where/which implementations in favor of unified helper
- Add Windows-specific DetectUv() override with enhanced uv/uvx detection
- Add WinGet shim path support for Windows uvx installation
- Update UI labels: "UV Path" -> "UVX Path"
- Only show uvx path status when override is configured

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

* fix: improve validation light(uvxPathStatus) logic for UVX path overrides and system paths

* refactor: streamline UV version validation and unify path detection methods across platform detectors

* fix: add type handling for Claude Code client in config JSON builder

* fix: correct command from 'uvx' to 'uv' for Python version listing in WindowsPlatformDetector

* feat: add uvx path fallback with warning UI

  - When override path is invalid, automatically fall back to system path
  - Add HasUvxPathFallback flag to track fallback state
  - Show yellow warning indicator when using fallback
  - Display clear messages for invalid override paths
  - Updated all platform detectors (Windows, macOS, Linux) to support fallback logic

* refactor: remove GetDetails method from PlatformDetectorBase

* Update ExecPath.cs

update Windows Path lookup

---------

Co-authored-by: ChatGLM 4.7 <noreply@zhipuai.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Shutong Wu <51266340+Scriptwonder@users.noreply.github.com>
2026-01-17 18:34:40 -05:00
dsarno cf177b50af
Replace asmdef GUID references (#564) 2026-01-15 07:52:53 -08:00
David Sarno 98796264ef Remove unnecessary allowNudge guard from PR #558
The allowNudge parameter was attempting to fix Unity 6 compilation loops
by skipping QueuePlayerLoopUpdate, but this was not the root cause.

The actual issue (fixed by #559's Unity 6+ preprocessor guard) is that
EditorApplication.update callbacks don't survive domain reloads properly
in Unity 6+.

Since #559 skips WaitForUnityReadyAsync entirely on Unity 6+ when compile
is requested, the allowNudge guard is redundant and can be removed.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-14 23:12:52 -08:00
Matthew 87d0f1d422
fix: Prevent infinite compilation loop in Unity 6 when using wait_for_ready (#559)
* fix: Prevent infinite compilation loop in Unity 6 when using wait_for_ready

Skip WaitForUnityReadyAsync when compileRequested is true. The
EditorApplication.update polling doesn't survive domain reloads
properly in Unity 6, causing infinite compilation loops.

When compilation is requested, return immediately and let the
client poll editor_state resource instead.

Fixes #557

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

* fix: Use actuallyWaited for hint message consistency

Address code review feedback: when compile was requested and we skip
WaitForUnityReadyAsync, the hint should correctly indicate that the
client needs to poll editor_state, not claim the editor is ready.

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

* refactor: Gate Unity 6 fix with version check and rename variable

Address code review feedback:
- Use UNITY_6000_0_OR_NEWER preprocessor directive to only apply the
  compilation wait bypass on Unity 6+, preserving original behavior
  for earlier versions
- Rename actuallyWaited to shouldWaitForReady for clarity, as it
  represents the decision to wait rather than a post-hoc result

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 23:12:30 -08:00
dsarno b6fa293cd3
Guard refresh wait nudge during compile (#558) 2026-01-14 20:14:44 -08:00
dsarno 3f08ac0e43
Fix local HTTP server UI check (#556)
* Fix local HTTP server UI check

* Fix connection section cleanup
2026-01-14 19:57:17 -08:00
GitHub Actions b611b268ef chore: bump version to 9.0.7 2026-01-15 00:10:23 +00:00
GitHub Actions 218f5f556d chore: bump version to 9.0.6 2026-01-14 23:54:36 +00:00
GitHub Actions 8df89587ec chore: bump version to 9.0.5 2026-01-14 23:46:48 +00:00
GitHub Actions 787e667c76 chore: bump version to 9.0.4 2026-01-14 23:07:07 +00:00
dsarno 39bff4ae27
Fix PlayMode tests stalling when unfocused (python refresh utility), improve domain reload recovery and refresh tool (#554)
* Fix test job state management after domain reload

- TestRunnerService.RunFinished: Always clean up job state even when
  _runCompletionSource is null (happens after PlayMode domain reload)
- TestJobManager: Detect and clear stale jobs (5+ min without updates)
  on startup to recover from stuck state after domain reload
- refresh_unity.py: Add "could not connect" to retryable errors when
  wait_for_ready=True, so connection failures during domain reload
  trigger waiting instead of immediate failure

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

* Add focus nudge to handle OS-level throttling during PlayMode tests

When Unity is unfocused, macOS App Nap (and similar OS features) can
throttle the process, causing PlayMode tests to stall even with Unity
No Throttling mode enabled.

Changes:
- Add ApplyNoThrottlingPreemptive() to TestRunnerNoThrottle for early
  throttle prevention before PlayMode Execute()
- Add focus_nudge.py utility that temporarily focuses Unity and returns
  focus to the original app (supports macOS, Windows, Linux)
- Integrate focus nudge into get_test_job polling - when tests appear
  stalled (unfocused + no progress for 10s), automatically nudge Unity

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

* Fix code review issues in focus_nudge.py

- Remove redundant time import (already imported at module level)
- Escape window titles in PowerShell script to prevent injection
- Remove unused Callable import

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

* Improve focus nudge logging and fix skipped tests

- Improve logging in focus_nudge.py: rate limit skip and focus return at INFO level
- Improve logging in run_tests.py: show nudge completion status
- Fix path resolution in test_logging_stdout.py and test_transport_framing.py
- Add PlayMode tests to UnityMCPTests project for testing PlayMode runner

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

* Add troubleshooting note about focus permission requests

When running PlayMode tests with Unity in the background, the focus
nudge feature may trigger OS permission prompts (especially on macOS
for accessibility/automation). Document this expected behavior.

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 15:02:40 -08:00
dsarno b874922cb0
Fix manage_components set_property for object references (#551) 2026-01-13 22:23:18 -08:00
Shutong Wu 53c0ed26ef
Refactor ClaudeCodeConfigurator to use JsonFileMcpConfigurator (#545)
The old CLI file map does not seem to work for the latest CC(V2.1.4), with errors that cannot recognize any symbols after uvx.exe, whether it be --no-cache, --refresh, or --from. I did not find any documentation change of why this cannot use.
I temporarily revert the current Claude Code registration to the old JSON way, which still works. Will look for a fix that could retain the CLI command usage.
2026-01-11 02:07:47 -05:00
Shutong Wu f2d56cfb22
Add support for 'standard' shader alias
Fix the no-parameter-material generation issue in URP setting
2026-01-10 23:17:44 -05:00
GitHub Actions 85198a07ea chore: bump version to 9.0.3 2026-01-08 16:01:52 +00:00
dsarno 7d58053f87
fix: replace Editor-only McpLog with Debug.LogWarning in Runtime assembly (#535)
- UnityTypeConverters.cs referenced McpLog (Editor-only) from Runtime asmdef
- This caused CS0103 build errors in player builds
- Replaced with UnityEngine.Debug.LogWarning for runtime compatibility

Also cleaned up test file:
- Removed stale NL test artifacts (Build marker, Tail test comments)
- Removed unused local functions causing CS8321 warnings
2026-01-08 07:59:05 -08:00
GitHub Actions bf6603d89e chore: bump version to 9.0.2 2026-01-08 15:18:45 +00:00
dsarno cba0933d33
Fixes Windows installation failures caused by long path issues when cloning the full repository via git URL (MAX_PATH 260 char limit exceeded by files in TestProjects/). (#534)
* fix: use PyPI as default server source instead of git URL

Fixes Windows installation failures caused by long path issues when
cloning the full repository (MAX_PATH 260 char limit exceeded).

- Change default from git+https://github.com/CoplayDev/unity-mcp to
  mcpforunityserver=={version} PyPI package
- Rename GetMcpServerGitUrl() to GetMcpServerPackageSource()
- Keep deprecated wrapper for backwards compatibility
- Update UI help text to show local dev override example only
- Update tests to expect PyPI package reference

* fix: use forward slashes in deployment path display

Fixes UI rendering issue where backslashes in Windows paths were
interpreted as escape sequences (e.g. \U, \u showing as boxes).

Convert backslashes to forward slashes for display in:
- Target path label
- Backup path label
2026-01-08 07:14:44 -08:00
GitHub Actions 275422e765 chore: bump version to 9.0.1 2026-01-08 05:50:55 +00:00
Marcus Sanatan a17df1e4c9
feat(batch_execute): improve error handling with success detection and fail-fast support (#531)
- Add DetermineCallSucceeded() to check command success via IMcpResponse interface or JObject/JToken 'success' field
- Track invocationFailureCount and set anyCommandFailed flag when commands fail
- Implement fail-fast behavior to stop batch execution on first failure when failFast=true
- Update commandResults to use computed callSucceeded value instead of hardcoded true

feat(game_object_create): enhance asset
2026-01-08 01:44:32 -04:00
GitHub Actions ea55c443bf chore: bump version to 9.0.0 2026-01-08 04:22:09 +00:00
dsarno cb4e2c9ef7
Fix HTTP/Stdio Transport UX and Test Bug (#530)
* refactor: Split ParseColorOrDefault into two overloads and change default to Color.white

* Auto-format Python code

* Remove unused Python module

* Refactored VFX functionality into multiple files

Tested everything, works like a charm

* Rename ManageVfx folder to just Vfx

We know what it's managing

* Clean up whitespace on plugin tools and resources

* Make ManageGameObject less of a monolith by splitting it out into different files

* Remove obsolete FindObjectByInstruction method

We also update the namespace for ManageVFX

* Add local test harness for fast developer iteration

Scripts for running the NL/T/GO test suites locally against a GUI Unity
Editor, complementing the CI workflows in .github/workflows/.

Benefits:
- 10-100x faster than CI (no Docker startup)
- Real-time Unity console debugging
- Single test execution for rapid iteration
- Auto-detects HTTP vs stdio transport

Usage:
  ./scripts/local-test/setup.sh           # One-time setup
  ./scripts/local-test/quick-test.sh NL-0 # Run single test
  ./scripts/local-test/run-nl-suite-local.sh  # Full suite

See scripts/local-test/README.md for details.

Also updated .gitignore to:
- Allow scripts/local-test/ to be tracked
- Ignore generated artifacts (reports/*.xml, .claude/local/, .unity-mcp/)

* Fix issue #525: Save dirty scenes for all test modes

Move SaveDirtyScenesIfNeeded() call outside the PlayMode conditional
so EditMode tests don't get blocked by Unity's "Save Scene" modal dialog.

This prevents MCP from timing out when running EditMode tests with unsaved
scene changes.

* refactor: Consolidate editor state resources into single canonical implementation

Merged EditorStateV2 into EditorState, making get_editor_state the canonical resource. Updated Unity C# to use EditorStateCache directly. Enhanced Python implementation with advice/staleness enrichment, external changes detection, and instance ID inference. Removed duplicate EditorStateV2 resource and legacy fallback mapping.

* Validate editor state with Pydantic models in both C# and Python

Added strongly-typed Pydantic models for EditorStateV2 schema in Python and corresponding C# classes with JsonProperty attributes. Updated C# to serialize using typed classes instead of anonymous objects. Python now validates the editor state payload before returning it, catching schema mismatches early.

* Consolidate run_tests and run_tests_async into single async implementation

Merged run_tests_async into run_tests, making async job-based execution the default behavior. Removed synchronous blocking test execution. Updated RunTests.cs to start test jobs immediately and return job_id for polling. Changed TestJobManager methods to internal visibility. Updated README to reflect single run_tests_async tool. Python implementation now uses async job pattern exclusively.

* Validate test job responses with Pydantic models in Python

* Change resources URI from unity:// to mcpforunity://

It should reduce conflicts with other Unity MCPs that users try, and to comply with Unity's requests regarding use of their company and product name

* Update README with all tools + better listing for resources

* Update other references to resources

* Updated translated doc - unfortunately I cannot verify

* Update the Chinese translation of the dev docks

* Change menu item from Setup Window to Local Setup Window

We now differentiate whether it's HTTP local or remote

* Fix URIs for menu items and tests

* Shouldn't have removed it

* fix: add missing FAST_FAIL_TIMEOUT constant in PluginHub

The FAST_FAIL_TIMEOUT class attribute was referenced on line 149 but never
defined, causing AttributeError on every ping attempt. This error was silently
caught by the broad 'except Exception' handler, causing all fast-fail commands
(read_console, get_editor_state, ping) to fail after 6 seconds of retries with
'ping not answered' error.

Added FAST_FAIL_TIMEOUT = 10 to define a 10-second timeout for fast-fail
commands, matching the intent of the existing fast-fail infrastructure.

* feat(ScriptableObject): enhance dry-run validation for AnimationCurve and Quaternion

Dry-run validation now validates value formats, not just property existence:

- AnimationCurve: Validates structure ({keys:[...]} or direct array), checks
  each keyframe is an object, validates numeric fields (time, value, inSlope,
  outSlope, inWeight, outWeight) and integer fields (weightedMode)
- Quaternion: Validates array length (3 for Euler, 4 for raw) or object
  structure ({x,y,z,w} or {euler:[x,y,z]}), ensures all components are numeric

Refactored shared validation helpers into appropriate locations:
- ParamCoercion: IsNumericToken, ValidateNumericField, ValidateIntegerField
- VectorParsing: ValidateAnimationCurveFormat, ValidateQuaternionFormat

Added comprehensive XML documentation clarifying keyframe field defaults
(all default to 0 except as noted).

Added 5 new dry-run validation tests covering valid and invalid formats
for both AnimationCurve and Quaternion properties.

* test: fix integration tests after merge

- test_refresh_unity_retry_recovery: Mock now handles both refresh_unity and
  get_editor_state commands (refresh_unity internally calls get_editor_state
  when wait_for_ready=True)
- test_run_tests_async_forwards_params: Mock response now includes required
  'mode' field for RunTestsStartResponse Pydantic validation
- test_get_test_job_forwards_job_id: Updated to handle GetTestJobResponse as
  Pydantic model instead of dict (use model_dump() for assertions)

* Update warning message to apply to all test modes

Follow-up to PR #527: Since SaveDirtyScenesIfNeeded() now runs for all test modes, update the warning message to say 'tests' instead of 'PlayMode tests'.

* feat(run_tests): add wait_timeout to get_test_job to avoid client loop detection

When polling for test completion, MCP clients like Cursor can detect the
repeated get_test_job calls as 'looping' and terminate the agent.

Added wait_timeout parameter that makes the server wait internally for tests
to complete (polling Unity every 2s) before returning. This dramatically
reduces client-side tool calls from 10-20 down to 1-2, avoiding loop detection.

Usage: get_test_job(job_id='xxx', wait_timeout=30)
- Returns immediately if tests complete within timeout
- Returns current status if timeout expires (client can call again)
- Recommended: 30-60 seconds

* fix: use Pydantic attribute access in test_run_tests_async for merge compatibility

* revert: remove local test harness - will be submitted in separate PR

* fix: stdio transport survives test runs without UI flicker

Root cause: WriteToConfigTests.TearDown() was unconditionally deleting
UseHttpTransport EditorPref even when tests were skipped on Windows
(NUnit runs TearDown even after Assert.Ignore).

Changes:
- Fix WriteToConfigTests to save/restore prefs instead of deleting
- Add centralized ShouldForceUvxRefresh() for local dev path detection
- Clean stale Python build/ artifacts before client configuration
- Improve reload handler flag management to prevent stuck Resuming state
- Show Resuming status during stdio bridge restart
- Initialize client config display on window open
- Add DevModeForceServerRefresh to EditorPrefs window known types

---------

Co-authored-by: Marcus Sanatan <msanatan@gmail.com>
Co-authored-by: Scott Jennings <scott.jennings+CIGINT@cloudimperiumgames.com>
2026-01-07 23:33:22 -04:00
Marcus Sanatan 34b6a11eb6
feat: Mark setup as completed when user clicks Done button (#529)
Closes #523
2026-01-07 18:58:36 -04:00
Marcus Sanatan c0fd7d50d4
v9 pre-release pruning (#528)
* refactor: Split ParseColorOrDefault into two overloads and change default to Color.white

* Auto-format Python code

* Remove unused Python module

* Refactored VFX functionality into multiple files

Tested everything, works like a charm

* Rename ManageVfx folder to just Vfx

We know what it's managing

* Clean up whitespace on plugin tools and resources

* Make ManageGameObject less of a monolith by splitting it out into different files

* Remove obsolete FindObjectByInstruction method

We also update the namespace for ManageVFX

* refactor: Consolidate editor state resources into single canonical implementation

Merged EditorStateV2 into EditorState, making get_editor_state the canonical resource. Updated Unity C# to use EditorStateCache directly. Enhanced Python implementation with advice/staleness enrichment, external changes detection, and instance ID inference. Removed duplicate EditorStateV2 resource and legacy fallback mapping.

* Validate editor state with Pydantic models in both C# and Python

Added strongly-typed Pydantic models for EditorStateV2 schema in Python and corresponding C# classes with JsonProperty attributes. Updated C# to serialize using typed classes instead of anonymous objects. Python now validates the editor state payload before returning it, catching schema mismatches early.

* Consolidate run_tests and run_tests_async into single async implementation

Merged run_tests_async into run_tests, making async job-based execution the default behavior. Removed synchronous blocking test execution. Updated RunTests.cs to start test jobs immediately and return job_id for polling. Changed TestJobManager methods to internal visibility. Updated README to reflect single run_tests_async tool. Python implementation now uses async job pattern exclusively.

* Validate test job responses with Pydantic models in Python

* Change resources URI from unity:// to mcpforunity://

It should reduce conflicts with other Unity MCPs that users try, and to comply with Unity's requests regarding use of their company and product name

* Update README with all tools + better listing for resources

* Update other references to resources

* Updated translated doc - unfortunately I cannot verify

* Update the Chinese translation of the dev docks

* Change menu item from Setup Window to Local Setup Window

We now differentiate whether it's HTTP local or remote

* Fix URIs for menu items and tests

* Shouldn't have removed it

* Minor edits from CodeRabbit feedback

* Don't use reflection which takes longer

* Fix failing python tests

* Add serialization helpers for ParticleSystem curves and MinMaxCurve types

Added SerializeAnimationCurve and SerializeMinMaxCurve helper methods to properly serialize Unity's curve types. Updated GetInfo to use these helpers for startLifetime, startSpeed, startSize, gravityModifier, and rateOverTime instead of only reading constant values.

* Use ctx param

* Update Server/src/services/tools/run_tests.py

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

* Minor fixes

* Rename anything EditorStateV2 to just EditorState

It's the default, there's no old version

* Make infer_single_instance_id public by removing underscore prefix

* Fix Python tests, again

* Replace AI generated .meta files with actual Unity ones

* ## Pre-Launch Enhancements: Testing Infrastructure & Tool Improvements (#8)

* Add local test harness for fast developer iteration

Scripts for running the NL/T/GO test suites locally against a GUI Unity
Editor, complementing the CI workflows in .github/workflows/.

Benefits:
- 10-100x faster than CI (no Docker startup)
- Real-time Unity console debugging
- Single test execution for rapid iteration
- Auto-detects HTTP vs stdio transport

Usage:
  ./scripts/local-test/setup.sh           # One-time setup
  ./scripts/local-test/quick-test.sh NL-0 # Run single test
  ./scripts/local-test/run-nl-suite-local.sh  # Full suite

See scripts/local-test/README.md for details.

Also updated .gitignore to:
- Allow scripts/local-test/ to be tracked
- Ignore generated artifacts (reports/*.xml, .claude/local/, .unity-mcp/)

* Fix issue #525: Save dirty scenes for all test modes

Move SaveDirtyScenesIfNeeded() call outside the PlayMode conditional
so EditMode tests don't get blocked by Unity's "Save Scene" modal dialog.

This prevents MCP from timing out when running EditMode tests with unsaved
scene changes.

* fix: add missing FAST_FAIL_TIMEOUT constant in PluginHub

The FAST_FAIL_TIMEOUT class attribute was referenced on line 149 but never
defined, causing AttributeError on every ping attempt. This error was silently
caught by the broad 'except Exception' handler, causing all fast-fail commands
(read_console, get_editor_state, ping) to fail after 6 seconds of retries with
'ping not answered' error.

Added FAST_FAIL_TIMEOUT = 10 to define a 10-second timeout for fast-fail
commands, matching the intent of the existing fast-fail infrastructure.

* feat(ScriptableObject): enhance dry-run validation for AnimationCurve and Quaternion

Dry-run validation now validates value formats, not just property existence:

- AnimationCurve: Validates structure ({keys:[...]} or direct array), checks
  each keyframe is an object, validates numeric fields (time, value, inSlope,
  outSlope, inWeight, outWeight) and integer fields (weightedMode)
- Quaternion: Validates array length (3 for Euler, 4 for raw) or object
  structure ({x,y,z,w} or {euler:[x,y,z]}), ensures all components are numeric

Refactored shared validation helpers into appropriate locations:
- ParamCoercion: IsNumericToken, ValidateNumericField, ValidateIntegerField
- VectorParsing: ValidateAnimationCurveFormat, ValidateQuaternionFormat

Added comprehensive XML documentation clarifying keyframe field defaults
(all default to 0 except as noted).

Added 5 new dry-run validation tests covering valid and invalid formats
for both AnimationCurve and Quaternion properties.

* test: fix integration tests after merge

- test_refresh_unity_retry_recovery: Mock now handles both refresh_unity and
  get_editor_state commands (refresh_unity internally calls get_editor_state
  when wait_for_ready=True)
- test_run_tests_async_forwards_params: Mock response now includes required
  'mode' field for RunTestsStartResponse Pydantic validation
- test_get_test_job_forwards_job_id: Updated to handle GetTestJobResponse as
  Pydantic model instead of dict (use model_dump() for assertions)

* Update warning message to apply to all test modes

Follow-up to PR #527: Since SaveDirtyScenesIfNeeded() now runs for all test modes, update the warning message to say 'tests' instead of 'PlayMode tests'.

* feat(run_tests): add wait_timeout to get_test_job to avoid client loop detection

When polling for test completion, MCP clients like Cursor can detect the
repeated get_test_job calls as 'looping' and terminate the agent.

Added wait_timeout parameter that makes the server wait internally for tests
to complete (polling Unity every 2s) before returning. This dramatically
reduces client-side tool calls from 10-20 down to 1-2, avoiding loop detection.

Usage: get_test_job(job_id='xxx', wait_timeout=30)
- Returns immediately if tests complete within timeout
- Returns current status if timeout expires (client can call again)
- Recommended: 30-60 seconds

* fix: use Pydantic attribute access in test_run_tests_async for merge compatibility

* revert: remove local test harness - will be submitted in separate PR

---------

Co-authored-by: Scott Jennings <scott.jennings+CIGINT@cloudimperiumgames.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: dsarno <david@lighthaus.us>
Co-authored-by: Scott Jennings <scott.jennings+CIGINT@cloudimperiumgames.com>
2026-01-07 18:51:51 -04:00