Skip to content

Conversation

@dsarno
Copy link
Collaborator

@dsarno dsarno commented Dec 4, 2025

Stdio was having problems switching instances. Should be fixed now.

Summary by CodeRabbit

  • Bug Fixes

    • Improved stability with defensive checks and exception handling to prevent crashes during cleanup operations
    • Enhanced instance selection with stricter validation and clearer error messaging for resolution failures
    • Strengthened error handling during logging configuration and session management
  • Documentation

    • Expanded debugging information with enhanced session state visibility and diagnostics

✏️ Tip: You can customize this high-level summary in your review settings.

- Ensure session persistence for stdio transport by using stable client_id/global fallback.

- Fix Nagle algorithm latency issues by setting TCP_NODELAY.

- Cleanup duplicate logging and imports.

- Resolve C# NullReferenceException in EditorWindow health check.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Walkthrough

Multiple refactoring and hardening changes across the codebase: defensive null-checks added to the editor window's health verification; middleware instantiation switched to a singleton pattern with thread-safe lazy initialization; transport detection abstracted into a centralized function; session key retrieval exposed as a public method on middleware; debug context expanded with middleware introspection; and logging improvements added to reduce noise and enhance error tracking.

Changes

Cohort / File(s) Summary
MCPForUnity Editor Defensive Programming
MCPForUnityEditorWindow.cs
Added null-check validation of window and connectionSection before invoking VerifyBridgeConnectionAsync in ScheduleHealthCheck; wrapped verification in try-catch to log warnings on exception instead of crashing during cleanup.
Middleware Lifecycle & Singleton Pattern
Server/src/transport/unity_instance_middleware.py, Server/src/main.py
Transitioned UnityInstanceMiddleware from direct instantiation to lazy-initialized singleton accessor via get_unity_instance_middleware(); added thread-safe initialization with RLock; promoted _get_session_key to public get_session_key method with new client_id-prioritized logic; enhanced logging for plugin resolution failures; improved exception handling in logging setup by replacing silent passes with debug logging.
Transport Detection Abstraction
Server/src/transport/unity_transport.py, Server/src/services/resources/unity_instances.py, Server/src/services/tools/set_active_instance.py
Introduced _current_transport() module-level function to centralize active transport detection; replaced all direct _is_http_transport() calls with _current_transport() comparisons; updated set_active_instance to use new transport variable in branching logic and expanded instance resolution with "@"-based exact matching and lowercased hash prefix searching; improved error responses for ambiguous or missing instance lookups.
Debug Instrumentation & Session Management
Server/src/services/tools/debug_request_context.py
Updated session key retrieval to use new public middleware.get_session_key(ctx) method; added PluginHub state introspection via PluginHub.is_configured(); expanded session_state output to include all_keys_in_store, plugin_hub_configured, and middleware_id for enhanced visibility into middleware internals.
Infrastructure Documentation
Server/src/transport/legacy/unity_connection.py
Added inline comment notes regarding config.unity_host trust and potential future behavior for localhost preference; no functional changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Specific areas requiring attention:
    • unity_instance_middleware.py: Verify thread-safety of RLock usage and lazy initialization correctness; confirm get_session_key logic with client_id prioritization is intentional
    • set_active_instance.py: Review new instance resolution logic (exact Name@hash matching and prefix search) for edge cases and error messaging clarity
    • main.py: Confirm logging propagation control changes do not suppress critical errors or expected diagnostic output
    • Cross-file consistency: Ensure all _current_transport() call sites correctly replace prior _is_http_transport() usage

Possibly related issues

Possibly related PRs

Poem

🐰 A singleton born under lock and key,
Transport streams flow so cleanly!
Middleware now speaks with public grace,
Null-checks guard the editor's space—
Session keys dance with purpose bright,
Refactored code shines pure and right! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main focus of the PR: fixing transport routing (HTTP/Stdio) and middleware session persistence, which are the primary themes across the multiple file changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ecf6cde and 059c693.

📒 Files selected for processing (1)
  • Server/src/transport/unity_instance_middleware.py (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Server/src/transport/unity_instance_middleware.py (1)
Server/src/services/tools/set_active_instance.py (1)
  • set_active_instance (15-112)
🪛 Ruff (0.14.7)
Server/src/transport/unity_instance_middleware.py

106-106: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (5)
Server/src/transport/unity_instance_middleware.py (5)

8-14: LGTM!

The logging setup follows standard Python practices with an appropriate module-level logger name.


19-31: LGTM! Thread-safety concern addressed.

The double-checked locking pattern correctly addresses the thread-safety issue raised in the previous review. The implementation prevents race conditions while avoiding unnecessary lock contention on subsequent calls.


68-84: LGTM!

All method calls consistently updated to use the public get_session_key API. The implementation is clean and thread-safe.


86-122: Consider catching specific exceptions for better error handling.

The defensive error handling aligns with the PR objectives to fix stdio instance switching. However, the broad Exception catch at line 106 could mask unexpected errors.

Consider catching specific exception types that PluginHub._resolve_session_id can raise. This would make error handling more explicit while maintaining resilience. If specific exceptions can be identified from the PluginHub implementation, refactor to catch only those known exceptions rather than all Exception types. This preserves resilience for expected failure modes while allowing unexpected errors to propagate for visibility.


53-66: Verify client_id availability and global fallback behavior in multi-client HTTP scenarios.

The session key derivation now prioritizes client_id and falls back to "global" when missing. This means all sessions without a client_id will share the same active Unity instance.

While this aligns with the PR objectives for stdio transport stability, please verify:

  1. That client_id is reliably set by the fastmcp Context in all expected scenarios (especially HTTP transport)
  2. That the "global" fallback doesn't cause unintended instance sharing in multi-client HTTP scenarios, or document the intentional constraints if this is expected behavior

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs (1)

217-231: Defensive checks and exception handling look solid; consider a tiny safety/clarity tweak

The added null-checks and try/catch around VerifyBridgeConnectionAsync correctly address the NRE/crash risk from EditorApplication.delayCall invoking this after the window or connectionSection has been torn down. Logging via McpLog.Warn is consistent with the rest of the file.

If you want to be extra explicit and slightly reduce any theoretical race between the null-check and use of the field, you could capture connectionSection into a local first:

EditorApplication.delayCall += async () =>
{
    var section = connectionSection;
    if (this == null || section == null)
    {
        return;
    }

    try
    {
        await section.VerifyBridgeConnectionAsync();
    }
    catch (Exception ex)
    {
        McpLog.Warn($"Health check verification failed: {ex.Message}");
    }
};

Not critical in the Unity editor single-threaded context, but a small clarity improvement if you touch this again.

Server/src/services/tools/set_active_instance.py (1)

68-73: Minor: Redundant getattr check.

The getattr(inst, "id", None) check on line 69 is redundant since matches is only populated from instances which were already filtered at line 47 to require an id. However, this is defensive and doesn't harm correctness.

         for inst in instances:
-            if not getattr(inst, "id", None):
-                continue
             inst_hash = getattr(inst, "hash", "")
             if inst_hash and inst_hash.lower().startswith(lookup):
                 matches.append(inst)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb9327c and ecf6cde.

⛔ Files ignored due to path filters (1)
  • Server/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • .github/workflows/github-repo-stats.yml (1 hunks)
  • MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs (1 hunks)
  • Server/src/main.py (3 hunks)
  • Server/src/services/resources/unity_instances.py (4 hunks)
  • Server/src/services/tools/debug_request_context.py (3 hunks)
  • Server/src/services/tools/set_active_instance.py (3 hunks)
  • Server/src/transport/legacy/unity_connection.py (1 hunks)
  • Server/src/transport/unity_instance_middleware.py (3 hunks)
  • Server/src/transport/unity_transport.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-13T13:27:23.040Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 316
File: TestProjects/UnityMCPTests/Assets/Tests/EditMode/Resources.meta:1-8
Timestamp: 2025-10-13T13:27:23.040Z
Learning: UnityMcpBridge is a legacy project kept for backwards compatibility; MCPForUnity is the only active Unity plugin project. GUID collisions between UnityMcpBridge and MCPForUnity are acceptable.

Applied to files:

  • Server/src/services/tools/debug_request_context.py
  • Server/src/services/tools/set_active_instance.py
  • Server/src/services/resources/unity_instances.py
🧬 Code graph analysis (6)
Server/src/services/tools/debug_request_context.py (1)
Server/src/transport/unity_instance_middleware.py (2)
  • get_session_key (50-63)
  • get_active_instance (71-75)
Server/src/main.py (1)
Server/src/transport/unity_instance_middleware.py (2)
  • UnityInstanceMiddleware (37-119)
  • get_unity_instance_middleware (21-28)
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs (1)
MCPForUnity/Editor/Helpers/McpLog.cs (2)
  • McpLog (7-52)
  • Warn (43-46)
Server/src/services/tools/set_active_instance.py (2)
Server/src/transport/unity_transport.py (1)
  • _current_transport (22-24)
Server/src/transport/unity_instance_middleware.py (3)
  • get_unity_instance_middleware (21-28)
  • set_active_instance (65-69)
  • get_session_key (50-63)
Server/src/services/resources/unity_instances.py (1)
Server/src/transport/unity_transport.py (1)
  • _current_transport (22-24)
Server/src/transport/unity_instance_middleware.py (1)
Server/src/services/tools/set_active_instance.py (1)
  • set_active_instance (15-112)
🪛 Ruff (0.14.7)
Server/src/main.py

57-57: Do not catch blind exception: Exception

(BLE001)


60-60: Do not catch blind exception: Exception

(BLE001)

Server/src/transport/unity_instance_middleware.py

103-103: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (17)
.github/workflows/github-repo-stats.yml (1)

4-4: Intentional disabling of scheduled trigger—LGTM.

The schedule trigger has been disabled while keeping workflow_dispatch active for manual runs. This aligns with the PR's focus on reducing automated runs and relying on explicit manual execution when needed.

Also applies to: 7-7

Server/src/transport/unity_transport.py (1)

22-25: LGTM!

Clean abstraction that exposes transport mode as a string identifier. This centralizes transport detection and provides a consistent API for other modules that need to branch on transport type.

Server/src/transport/legacy/unity_connection.py (1)

63-66: LGTM!

Good documentation of the current trust model for config.unity_host. The note about future OS resolver behavior consideration is helpful for maintainability.

Server/src/services/resources/unity_instances.py (1)

39-40: LGTM!

Clean refactor to use centralized _current_transport(). Storing the result in a local variable and using it in both the conditional and response payload ensures consistency and avoids redundant function calls.

Also applies to: 75-75, 102-102

Server/src/services/tools/debug_request_context.py (2)

41-47: Debug-only exposure of middleware internals is acceptable.

Accessing middleware._active_by_key under lock is thread-safe, and exposing all_keys_in_store, plugin_hub_configured, and middleware_id is valuable for debugging session persistence issues. Since this is explicitly a debug endpoint, the internal detail exposure is appropriate.


39-39: Good: Using public API instead of private method.

The change from middleware._get_session_key(ctx) to middleware.get_session_key(ctx) correctly uses the public API as indicated in the relevant code snippets from unity_instance_middleware.py.

Server/src/services/tools/set_active_instance.py (3)

19-22: LGTM!

Clean refactor to use centralized _current_transport() for transport detection, consistent with the pattern in other files.


57-89: Robust instance resolution with clear error messages.

The two-path resolution logic (exact Name@hash match vs. hash prefix matching) with explicit ambiguity handling is well-structured. Error messages guide users to unity://instances for correct identifiers.


99-111: LGTM!

Including session_key in the response is helpful for debugging session persistence issues, which aligns with the PR objective of fixing stdio instance switching.

Server/src/main.py (4)

50-50: Good fix for double-logging issue.

Setting propagate = False on both the main logger and telemetry logger prevents log messages from bubbling up to the root logger when a file handler is attached, addressing the duplicate logging mentioned in PR objectives.

Also applies to: 56-56


57-62: Improved exception handling during logging setup.

Replacing bare pass with logger.debug(..., exc_info=exc) preserves diagnostic information while maintaining startup robustness. The broad Exception catches are intentional here to ensure logging configuration issues never break server startup.


64-68: Good addition of noisy logger suppression.

Adding "mcp.server.lowlevel.server" to the noisy logger list and setting propagate = False helps reduce clutter during stdio handshake, improving startup log readability.


267-269: LGTM! Correct use of singleton accessor.

Using get_unity_instance_middleware() instead of direct instantiation ensures the same middleware instance is used across the codebase, which is essential for session persistence. This aligns with the changes in unity_instance_middleware.py where the singleton pattern was introduced.

Server/src/transport/unity_instance_middleware.py (4)

8-14: LGTM: Logging setup is appropriate.

The logging configuration follows standard Python practices and will help with debugging session persistence issues.


67-81: LGTM: Consistent use of the renamed session key method.

All call sites have been correctly updated to use get_session_key() with proper locking and session key derivation.


50-63: Code intent is already clear in documentation; no verification needed.

The "global" fallback and single-user local mode assumption are explicitly documented in the method's docstring (lines 52–58) and inline comment (line 62). This is intentional design for local development, not a hidden constraint. The code is sufficiently clear about its scope and limitations.


83-119: Consider more specific exception handling if possible.

The defensive approach of not clearing active_instance on PluginHub resolution failure (lines 103-114) is reasonable for stdio transport compatibility. However, catching blind Exception (line 103) is flagged by static analysis and should be more specific if feasible.

To determine the appropriate exception types, inspect PluginHub._resolve_session_id() to identify what it explicitly raises. If specific exceptions can be identified (e.g., custom PluginHub exceptions, ConnectionError, etc.), replace the broad Exception handler with those types. If the method can raise unpredictable exceptions or if broad handling is intentional for resilience, document this rationale as a comment to suppress static analysis warnings if necessary.

@dsarno dsarno merged commit d93e437 into CoplayDev:main Dec 4, 2025
1 check passed
@dsarno dsarno deleted the fix-http-stdio-transport branch December 8, 2025 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Is _resolve_session_id expected to be called in STDIO mode?

1 participant