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.
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
- # Use the same defaults as the stdio transport reload handling so that
365
- # HTTP/WebSocket and TCP behave consistently without per-project env.
366
- max_retries = max(1, int(getattr(config, "reload_max_retries", 40)))
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() + (max_retries * sleep_seconds)
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
- f"No plugin session available (instance={unity_instance or 'default'}); waiting up to {deadline - wait_started:.2f}s",
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
- f"Plugin session restored after {time.monotonic() - wait_started:.3f}s (instance={unity_instance or 'default'})",
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
- f"No Unity plugin reconnected within {max_retries * sleep_seconds:.2f}s (instance={unity_instance or 'default'})",
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 RuntimeError("No Unity plugins are currently connected")
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
- session_id = await cls._resolve_session_id(unity_instance)
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 Exception:
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: