Skip to content

Conversation

@dsarno
Copy link
Owner

@dsarno dsarno commented Nov 21, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added HTTP transport support for discovering and connecting to Unity instances.
    • Improved instance identification supporting both Name@hash and hash-only formats.
  • Improvements

    • Session management now operates on a per-project basis for better isolation.
    • Enhanced error handling and fallback logic for instance discovery operations.

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

@cursor
Copy link

cursor bot commented Nov 21, 2025

You have run out of free Bugbot PR reviews for this billing cycle. This will reset on December 7.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

The changes implement per-project session scoping in Unity and add HTTP-based transport support for instance discovery and routing. Session IDs are now stored with project-specific hash-derived keys. Server-side instance resolution and active instance management are enhanced to support HTTP transport via PluginHub alongside existing stdio/TCP pools, with Name@hash instance identification support.

Changes

Cohort / File(s) Change Summary
Project-scoped session management
MCPForUnity/Editor/Helpers/ProjectIdentityUtility.cs
GetOrCreateSessionId and ResetSessionId now derive session keys from project-specific hashes (SessionPrefKey_). Methods initialize identity cache before key operations and store/clear per-project session GUIDs instead of using global keys.
Server-side instance routing and resolution
Server/plugin_hub.py
Adds parsing of unity_instance in Name@hash or bare-hash formats, extracting target_hash when applicable. Resolution now prefers querying via target_hash using get_session_id_by_hash() when available, with fallback to listing and selecting first available session.
HTTP transport support
Server/resources/unity_instances.py, Server/tools/set_active_instance.py
Introduces HTTP transport path for instance discovery via PluginHub.get_sessions() alongside existing stdio/TCP pool discovery. Adds _is_http_transport() helper and constructs instances with id, name, hash, unity_version, connected_at, session_id fields. Implements duplicate project name detection with warnings. Enhanced error handling for both transport modes. set_active_instance converted from sync to async function.
Integration tests for HTTP transport
Server/tests/integration/test_instance_routing_comprehensive.py
Adds TestInstanceRoutingHTTP class with two async tests: test_set_active_instance_http_transport (mocks HTTP transport and PluginHub) and test_set_active_instance_http_hash_only (tests hash-only resolution). Imports set_active_instance as tool. Note: test class defined in two distinct locations within the file.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant SetActiveInstance as set_active_instance.py
    participant PluginHub
    participant UnityPool as Unity Connection Pool
    participant Middleware

    Client->>SetActiveInstance: set_active_instance(instance: "Project@hash" or "hash")
    
    alt HTTP Transport
        SetActiveInstance->>PluginHub: get_sessions()
        PluginHub-->>SetActiveInstance: [session_data, ...]
        SetActiveInstance->>SetActiveInstance: Build instances from sessions
    else Stdio Transport
        SetActiveInstance->>UnityPool: discover_all_instances()
        UnityPool-->>SetActiveInstance: [instance_data, ...]
    end
    
    SetActiveInstance->>SetActiveInstance: Resolve instance ID/hash from input
    SetActiveInstance->>SetActiveInstance: Match against discovered instances
    
    alt Match Found
        SetActiveInstance->>Middleware: Store active instance
        Middleware-->>SetActiveInstance: OK
        SetActiveInstance-->>Client: {success: true}
    else No Match
        SetActiveInstance-->>Client: {error: "Instance not found"}
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Session scoping implications: Verify ProjectIdentityUtility changes correctly isolate sessions per project without impacting existing session management or causing migration issues
  • HTTP vs. stdio transport branching: Ensure both discovery paths work correctly, duplicate detection is consistent, and error handling covers edge cases in both modes
  • Async conversion in set_active_instance: Review the synchronous-to-asynchronous conversion for proper await handling, exception propagation, and context preservation
  • Duplicate test class definition: The test file appears to define TestInstanceRoutingHTTP in two locations—clarify if this is intentional or a merge artifact
  • PluginHub integration: Verify get_sessions() contract matches expected session data structure and null/empty handling

Possibly related PRs

Suggested reviewers

  • msanatan

Poem

🐰 Session keys now bloom per project so fair,
HTTP whispers through the networked air,
Async routines hop and skip with grace,
PluginHub discovers each instance's place,
Transport-aware routing finds its way—
One unified path for instances today! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% 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 reflects the main changes across the pull request, which introduce HTTP instance routing support and modify session IDs to be per-project scoped.

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

@dsarno
Copy link
Owner Author

dsarno commented Nov 21, 2025

@CodeRabbit please review

@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

@dsarno: I'll review the changes in this pull request.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@greptile-apps
Copy link

greptile-apps bot commented Nov 21, 2025

Greptile Overview

Greptile Summary

Made session IDs project-specific by scoping EditorPrefs keys with project hashes, and fixed HTTP transport instance routing to properly parse Name@hash format.

Key Changes:

  • Session IDs now scoped per-project using MCPForUnity.WebSocketSessionId_{projectHash} keys instead of global key
  • PluginHub._resolve_session_id now extracts hash from Name@hash format to match get_session_id_by_hash expectations
  • unity_instances resource and set_active_instance tool now branch based on transport type (HTTP vs stdio)
  • Added comprehensive test coverage for HTTP transport routing scenarios

Issues Found:

  • If Application.dataPath is unavailable during initialization, multiple projects could share the _default session ID key, breaking per-project isolation

Confidence Score: 4/5

  • Safe to merge with one edge case that needs monitoring in production
  • The HTTP routing logic is well-tested and correctly implemented. The C# session ID scoping works correctly in normal scenarios, but has a logical flaw where projects that fail to compute their hash would share the same session ID key. This is an edge case that would only occur if Application.dataPath is unavailable during Unity initialization, which is rare but possible during certain startup conditions or in test environments
  • Pay attention to MCPForUnity/Editor/Helpers/ProjectIdentityUtility.cs for the edge case where hash computation fails

Important Files Changed

File Analysis

Filename Score Overview
MCPForUnity/Editor/Helpers/ProjectIdentityUtility.cs 4/5 Made session IDs project-specific by scoping EditorPrefs keys with project hash; cache initialization logic updated to ensure hash is available
Server/plugin_hub.py 5/5 Added parsing logic to extract hash from Name@hash format in _resolve_session_id for consistent instance resolution
Server/resources/unity_instances.py 5/5 Added HTTP transport detection and branching logic to query PluginHub sessions vs connection pool based on transport type
Server/tools/set_active_instance.py 5/5 Refactored to support both HTTP and stdio transports with proper instance enumeration from PluginHub or connection pool
Server/tests/integration/test_instance_routing_comprehensive.py 5/5 Added comprehensive test coverage for HTTP transport instance routing including full ID and hash-only selection

Sequence Diagram

sequenceDiagram
    participant Client as MCP Client
    participant Middleware as UnityInstanceMiddleware
    participant Tool as set_active_instance
    participant Hub as PluginHub
    participant Registry as PluginRegistry
    participant Unity as Unity Editor

    Note over Client,Unity: HTTP Transport Mode
    
    Unity->>Hub: WebSocket connect + register(session_id, project_hash)
    Hub->>Registry: register(session_id, project_name, project_hash)
    Registry-->>Hub: PluginSession
    
    Client->>Tool: set_active_instance("ProjectName@hash")
    Tool->>Hub: get_sessions()
    Hub->>Registry: list_sessions()
    Registry-->>Hub: sessions dict
    Hub-->>Tool: sessions with project, hash
    
    alt Full ID format (Name@hash)
        Tool->>Tool: Match against inst.id
    else Hash only
        Tool->>Tool: Match against inst.hash.startswith(value)
    end
    
    Tool->>Middleware: set_active_instance(ctx, resolved_id)
    Tool-->>Client: success + resolved instance ID
    
    Client->>Hub: send_command_for_instance(unity_instance, cmd, params)
    Hub->>Hub: _resolve_session_id(unity_instance)
    
    alt unity_instance contains "@"
        Hub->>Hub: Extract hash from "Name@hash"
    else
        Hub->>Hub: Use as-is (hash only)
    end
    
    Hub->>Registry: get_session_id_by_hash(target_hash)
    Registry-->>Hub: session_id
    Hub->>Unity: send_command(session_id, cmd, params)
    Unity-->>Hub: command_result
    Hub-->>Client: result
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 132 to 135
if (_cachedProjectHash == "default" || string.IsNullOrEmpty(_cachedProjectHash))
{
UpdateIdentityCache();
}
Copy link

Choose a reason for hiding this comment

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

logic: If Application.dataPath is unavailable, UpdateIdentityCache() returns early (line 52-54) leaving _cachedProjectHash as "default". This check will always be true in that scenario, but subsequent GetProjectHash() will still return "default". Multiple projects failing hash computation would share MCPForUnity.WebSocketSessionId_default, violating per-project session ID isolation

Prompt To Fix With AI
This is a comment left during a code review.
Path: MCPForUnity/Editor/Helpers/ProjectIdentityUtility.cs
Line: 132:135

Comment:
**logic:** If `Application.dataPath` is unavailable, `UpdateIdentityCache()` returns early (line 52-54) leaving `_cachedProjectHash` as "default". This check will always be true in that scenario, but subsequent `GetProjectHash()` will still return "default". Multiple projects failing hash computation would share `MCPForUnity.WebSocketSessionId_default`, violating per-project session ID isolation

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@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: 0

🧹 Nitpick comments (6)
MCPForUnity/Editor/Helpers/ProjectIdentityUtility.cs (2)

125-145: Per-project session key derivation looks solid; consider clarifying the “default” fallback behavior

Deriving projectSpecificKey from _cachedProjectHash and forcing a cache refresh when it is "default" or empty gives you proper per-project isolation in the common case and avoids relying on the async initializer.

One edge-case to be aware of: if UpdateIdentityCache() cannot ever compute a real hash (e.g., Application.dataPath missing or consistently failing), multiple such projects will still share SessionPrefKey_default. If that scenario matters (batch runners, unusual editor setups), you might want to either document that behavior, or add a secondary discriminator (e.g., project name/path) before falling back to "default".


163-174: ResetSessionId correctly scopes deletion to the current project; optional legacy-cleanup

Mirroring the on-demand cache refresh and project-hash-based key in ResetSessionId ensures tests only clear the current project’s session id rather than a global key, which matches the new per-project semantics.

If you want a clean migration from the old global key, you could optionally also delete the legacy SessionPrefKey (without hash suffix) once per process so that stale global ids don’t linger in EditorPrefs, while keeping the main behavior as-is.

Server/plugin_hub.py (1)

225-238: Name@hash parsing and hash-based lookup align with routing intent

Allowing unity_instance to be either bare hash or Name@hash and normalizing both into target_hash for get_session_id_by_hash keeps the API flexible while preserving deterministic selection when a specific hash is given.

One minor future-proofing tweak you might consider: if project names are ever allowed to contain '@', unity_instance.partition("@") will split on the first delimiter and treat the rest (including any additional '@') as the hash. Using unity_instance.rpartition("@") (or rsplit("@", 1)) would instead always take the last segment as the hash while leaving any earlier '@' in the name.

Server/tests/integration/test_instance_routing_comprehensive.py (1)

19-244: HTTP routing tests accurately exercise the new discovery path

The HTTP tests are wired up well: they set UNITY_MCP_TRANSPORT="http", patch tools.set_active_instance.PluginHub.get_sessions and get_unity_instance_middleware, and then assert that set_active_instance_tool updates UnityInstanceMiddleware to the expected Project@hash for both explicit Name@hash and hash-only inputs. That directly validates the new HTTP-aware path in set_active_instance.

If you want to harden behavior further, you could add one or two cases where the hash prefix is ambiguous or doesn’t exist, asserting that the error messages (and success flags) match what the stdio path already guarantees. That would help ensure both transports stay behaviorally aligned as the routing logic evolves.

Server/tools/set_active_instance.py (1)

2-48: Async, transport-aware discovery is correct; tighten a couple of edge cases

The refactor to make set_active_instance async and to branch on _is_http_transport() looks good: the HTTP code path pulls sessions from plugin_hub.PluginHub.get_sessions(), normalizes them into SimpleNamespace objects with id/hash/name, and then reuses the existing Name@hash / hash-prefix resolution and middleware storage logic. The stdio path continues to rely on get_unity_connection_pool().discover_all_instances(force_refresh=True), so existing behavior is preserved when not using HTTP.

Two small improvements worth considering:

  • Input validation: when instance is empty or only whitespace, value = instance.strip() becomes "" and the hash-prefix branch will see every instance as a match (startswith("")), returning the “matches multiple instances” error. A short early check for if not value: returning a clearer validation error would make this behavior more intentional.
  • Transport detection reuse: _is_http_transport() is now duplicated here and in Server/resources/unity_instances.py. Pulling this into a tiny shared helper module (or config utility) would avoid drift if you ever introduce additional transports or rename "http"/"stdio".

Neither of these blocks the current behavior, but they would make the tool a bit more predictable and maintainable.

Server/resources/unity_instances.py (1)

13-105: HTTP/stdio branches produce a consistent API; just be explicit about PluginHub schema coupling

The transport split is clean: when _is_http_transport() is true you query plugin_hub.PluginHub.get_sessions() and build instances from its session dict, and otherwise you fall back to get_unity_connection_pool().discover_all_instances(...). Both paths return the same top-level shape (success, transport, instance_count, instances, optional warning), and the duplicate-name warning text is shared, which is helpful for clients.

One thing to keep in mind is that the HTTP branch assumes PluginHub.get_sessions() always returns 'project' and 'hash' keys (accessed via session_info["project"] / ["hash"]). Today that’s true, but if the PluginHub schema ever changes, this will surface as a KeyError that then gets wrapped by the outer except block. To make that coupling clearer and a bit safer you could either:

  • Switch to .get with an explicit check and a targeted error message when the schema is malformed, or
  • Add a short comment here stating that PluginHub.get_sessions is the canonical producer of this structure.

Also, _is_http_transport() duplicates the helper in Server/tools/set_active_instance.py; sharing a single implementation would reduce the chance of the two drifting apart in future changes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5396dff and 8a2e04a.

📒 Files selected for processing (5)
  • MCPForUnity/Editor/Helpers/ProjectIdentityUtility.cs (2 hunks)
  • Server/plugin_hub.py (1 hunks)
  • Server/resources/unity_instances.py (2 hunks)
  • Server/tests/integration/test_instance_routing_comprehensive.py (2 hunks)
  • Server/tools/set_active_instance.py (1 hunks)

@dsarno dsarno force-pushed the fix/http-instance-routing branch from 8a2e04a to 2c865bf Compare November 21, 2025 04:58
@dsarno dsarno closed this Nov 22, 2025
@dsarno dsarno deleted the fix/http-instance-routing branch November 22, 2025 16:41
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.

2 participants