mcpforunityserver 8.6.0__py3-none-any.whl → 8.7.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mcpforunityserver-8.6.0.dist-info → mcpforunityserver-8.7.1.dist-info}/METADATA +2 -2
- {mcpforunityserver-8.6.0.dist-info → mcpforunityserver-8.7.1.dist-info}/RECORD +18 -13
- services/resources/editor_state_v2.py +270 -0
- services/state/external_changes_scanner.py +246 -0
- services/tools/manage_asset.py +7 -0
- services/tools/manage_gameobject.py +5 -0
- services/tools/manage_scene.py +4 -0
- services/tools/preflight.py +107 -0
- services/tools/read_console.py +25 -15
- services/tools/refresh_unity.py +90 -0
- services/tools/run_tests.py +22 -3
- services/tools/test_jobs.py +94 -0
- transport/legacy/unity_connection.py +101 -16
- transport/plugin_hub.py +47 -11
- {mcpforunityserver-8.6.0.dist-info → mcpforunityserver-8.7.1.dist-info}/WHEEL +0 -0
- {mcpforunityserver-8.6.0.dist-info → mcpforunityserver-8.7.1.dist-info}/entry_points.txt +0 -0
- {mcpforunityserver-8.6.0.dist-info → mcpforunityserver-8.7.1.dist-info}/licenses/LICENSE +0 -0
- {mcpforunityserver-8.6.0.dist-info → mcpforunityserver-8.7.1.dist-info}/top_level.txt +0 -0
transport/plugin_hub.py
CHANGED
|
@@ -34,6 +34,10 @@ class PluginDisconnectedError(RuntimeError):
|
|
|
34
34
|
"""Raised when a plugin WebSocket disconnects while commands are in flight."""
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
class NoUnitySessionError(RuntimeError):
|
|
38
|
+
"""Raised when no Unity plugins are available."""
|
|
39
|
+
|
|
40
|
+
|
|
37
41
|
class PluginHub(WebSocketEndpoint):
|
|
38
42
|
"""Manages persistent WebSocket connections to Unity plugins."""
|
|
39
43
|
|
|
@@ -361,11 +365,20 @@ class PluginHub(WebSocketEndpoint):
|
|
|
361
365
|
if cls._registry is None:
|
|
362
366
|
raise RuntimeError("Plugin registry not configured")
|
|
363
367
|
|
|
364
|
-
#
|
|
365
|
-
|
|
366
|
-
|
|
368
|
+
# Bound waiting for Unity sessions so calls fail fast when editors are not ready.
|
|
369
|
+
try:
|
|
370
|
+
max_wait_s = float(
|
|
371
|
+
os.environ.get("UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S", "2.0"))
|
|
372
|
+
except ValueError as e:
|
|
373
|
+
raw_val = os.environ.get("UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S", "2.0")
|
|
374
|
+
logger.warning(
|
|
375
|
+
"Invalid UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S=%r, using default 2.0: %s",
|
|
376
|
+
raw_val, e)
|
|
377
|
+
max_wait_s = 2.0
|
|
378
|
+
# Clamp to [0, 30] to prevent misconfiguration from causing excessive waits
|
|
379
|
+
max_wait_s = max(0.0, min(max_wait_s, 30.0))
|
|
367
380
|
retry_ms = float(getattr(config, "reload_retry_ms", 250))
|
|
368
|
-
sleep_seconds = max(0.05, retry_ms / 1000.0)
|
|
381
|
+
sleep_seconds = max(0.05, min(0.25, retry_ms / 1000.0))
|
|
369
382
|
|
|
370
383
|
# Allow callers to provide either just the hash or Name@hash
|
|
371
384
|
target_hash: str | None = None
|
|
@@ -394,7 +407,7 @@ class PluginHub(WebSocketEndpoint):
|
|
|
394
407
|
return None, count
|
|
395
408
|
|
|
396
409
|
session_id, session_count = await _try_once()
|
|
397
|
-
deadline = time.monotonic() +
|
|
410
|
+
deadline = time.monotonic() + max_wait_s
|
|
398
411
|
wait_started = None
|
|
399
412
|
|
|
400
413
|
# If there is no active plugin yet (e.g., Unity starting up or reloading),
|
|
@@ -408,14 +421,18 @@ class PluginHub(WebSocketEndpoint):
|
|
|
408
421
|
if wait_started is None:
|
|
409
422
|
wait_started = time.monotonic()
|
|
410
423
|
logger.debug(
|
|
411
|
-
|
|
424
|
+
"No plugin session available (instance=%s); waiting up to %.2fs",
|
|
425
|
+
unity_instance or "default",
|
|
426
|
+
max_wait_s,
|
|
412
427
|
)
|
|
413
428
|
await asyncio.sleep(sleep_seconds)
|
|
414
429
|
session_id, session_count = await _try_once()
|
|
415
430
|
|
|
416
431
|
if session_id is not None and wait_started is not None:
|
|
417
432
|
logger.debug(
|
|
418
|
-
|
|
433
|
+
"Plugin session restored after %.3fs (instance=%s)",
|
|
434
|
+
time.monotonic() - wait_started,
|
|
435
|
+
unity_instance or "default",
|
|
419
436
|
)
|
|
420
437
|
if session_id is None and not target_hash and session_count > 1:
|
|
421
438
|
raise RuntimeError(
|
|
@@ -425,11 +442,13 @@ class PluginHub(WebSocketEndpoint):
|
|
|
425
442
|
|
|
426
443
|
if session_id is None:
|
|
427
444
|
logger.warning(
|
|
428
|
-
|
|
445
|
+
"No Unity plugin reconnected within %.2fs (instance=%s)",
|
|
446
|
+
max_wait_s,
|
|
447
|
+
unity_instance or "default",
|
|
429
448
|
)
|
|
430
449
|
# At this point we've given the plugin ample time to reconnect; surface
|
|
431
450
|
# a clear error so the client can prompt the user to open Unity.
|
|
432
|
-
raise
|
|
451
|
+
raise NoUnitySessionError("No Unity plugins are currently connected")
|
|
433
452
|
|
|
434
453
|
return session_id
|
|
435
454
|
|
|
@@ -440,7 +459,20 @@ class PluginHub(WebSocketEndpoint):
|
|
|
440
459
|
command_type: str,
|
|
441
460
|
params: dict[str, Any],
|
|
442
461
|
) -> dict[str, Any]:
|
|
443
|
-
|
|
462
|
+
try:
|
|
463
|
+
session_id = await cls._resolve_session_id(unity_instance)
|
|
464
|
+
except NoUnitySessionError:
|
|
465
|
+
logger.debug(
|
|
466
|
+
"Unity session unavailable; returning retry: command=%s instance=%s",
|
|
467
|
+
command_type,
|
|
468
|
+
unity_instance or "default",
|
|
469
|
+
)
|
|
470
|
+
return MCPResponse(
|
|
471
|
+
success=False,
|
|
472
|
+
error="Unity session not available; please retry",
|
|
473
|
+
hint="retry",
|
|
474
|
+
data={"reason": "no_unity_session", "retry_after_ms": 250},
|
|
475
|
+
).model_dump()
|
|
444
476
|
|
|
445
477
|
# During domain reload / immediate reconnect windows, the plugin may be connected but not yet
|
|
446
478
|
# ready to process execute commands on the Unity main thread (which can be further delayed when
|
|
@@ -450,7 +482,11 @@ class PluginHub(WebSocketEndpoint):
|
|
|
450
482
|
if command_type in cls._FAST_FAIL_COMMANDS and command_type != "ping":
|
|
451
483
|
try:
|
|
452
484
|
max_wait_s = float(os.environ.get("UNITY_MCP_SESSION_READY_WAIT_SECONDS", "6"))
|
|
453
|
-
except
|
|
485
|
+
except ValueError as e:
|
|
486
|
+
raw_val = os.environ.get("UNITY_MCP_SESSION_READY_WAIT_SECONDS", "6")
|
|
487
|
+
logger.warning(
|
|
488
|
+
"Invalid UNITY_MCP_SESSION_READY_WAIT_SECONDS=%r, using default 6.0: %s",
|
|
489
|
+
raw_val, e)
|
|
454
490
|
max_wait_s = 6.0
|
|
455
491
|
max_wait_s = max(0.0, min(max_wait_s, 30.0))
|
|
456
492
|
if max_wait_s > 0:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|