Skip to content

Comments

Display resources#658

Merged
msanatan merged 6 commits intoCoplayDev:betafrom
msanatan:display-resources
Jan 31, 2026
Merged

Display resources#658
msanatan merged 6 commits intoCoplayDev:betafrom
msanatan:display-resources

Conversation

@msanatan
Copy link
Member

@msanatan msanatan commented Jan 31, 2026

Description

Shows the resources in the tab & fixes the enable/disable function for built-in tools

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)

Changes Made

Add a tab for displaying resources, similar to how we had a tab for displaying tools. Also, when a user disables a tool or resource, we return an error to the LLM letting them know the tool was disabled and they shouldn't use it.

Testing/Screenshots/Recordings

Screenshot 2026-01-30 at 7 28 06 PM Screenshot 2026-01-30 at 7 31 56 PM

Documentation Updates

  • I have added/removed/modified tools or resources
  • If yes, I have updated all documentation files using:
    • The LLM prompt at tools/UPDATE_DOCS_PROMPT.md (recommended)
    • Manual updates following the guide at tools/UPDATE_DOCS.md

Related Issues

Closes #648

Additional Notes

Summary by Sourcery

Add resource management to the MCP for Unity editor and ensure disabled tools and resources are respected across the Unity editor and server.

New Features:

  • Introduce a Resources tab in the MCP for Unity editor window with discovery, categorization, and per-resource enable/disable controls.
  • Add a reflection-based resource discovery service with metadata, built-in detection, caching, and editor preference storage for resource state.
  • Expose a description field on MCP resources for clearer presentation in the editor UI.

Bug Fixes:

  • Prevent execution of disabled tools and resources by short-circuiting Unity transport commands with an explicit error response.
  • Handle error-shaped Unity resource responses on the server without causing Pydantic validation errors for typed resource response models.

Enhancements:

  • Share built-in type detection logic via a reusable helper used by both tool and resource discovery.
  • Normalize server-side resource service functions to use a common response parsing helper for consistent error handling.

Summary by CodeRabbit

  • New Features

    • Added a Resources tab with a Resources section showing Built-in vs Custom resources, descriptions, per-resource toggles, persisted foldouts, Rescan, and Bulk Enable/Disable actions.
    • Resource discovery and enable/disable state persisted and refreshable from the UI.
  • Bug Fixes / Behavior

    • Commands for disabled resources or tools are now blocked with a clear error to prevent execution.

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

…ndling

- Block execution of disabled resources in TransportCommandDispatcher with clear error message
- Add parse_resource_response() utility to handle error responses without Pydantic validation failures
- Replace inline response parsing with parse_resource_response() across all resource handlers
- Export parse_resource_response from models/__init__.py for consistent usage
…her with clear error message

Add tool enable/disable enforcement before command execution. Check tool metadata and enabled state, returning error response if tool is disabled. Prevents execution of disabled tools with user-friendly error message.
@msanatan msanatan self-assigned this Jan 31, 2026
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 31, 2026

Reviewer's Guide

Adds a new Resources tab to the MCP for Unity editor mirroring the Tools tab, introduces a reflection-based resource discovery service with enable/disable persistence, and ensures both tools and resources are blocked at dispatch-time when disabled, while tightening Python server-side resource response parsing to gracefully handle error payloads.

Sequence diagram for dispatch-time blocking of disabled tools and resources

sequenceDiagram
    participant LLM
    participant Server
    participant UnityEditor
    participant TransportCommandDispatcher as TransportDispatcher
    participant MCPServiceLocator as ServiceLocator
    participant ResourceDiscovery as ResourceDiscoveryService
    participant ToolDiscovery as ToolDiscoveryService

    LLM->>Server: send MCP command
    Server->>UnityEditor: MCP command request
    UnityEditor->>TransportDispatcher: ProcessCommand(command)

    TransportDispatcher->>ServiceLocator: ResourceDiscovery
    ServiceLocator-->>TransportDispatcher: IResourceDiscoveryService
    TransportDispatcher->>ResourceDiscovery: GetResourceMetadata(command.type)
    ResourceDiscovery-->>TransportDispatcher: ResourceMetadata or null
    alt resource metadata exists and disabled
        TransportDispatcher->>ResourceDiscovery: IsResourceEnabled(command.type)
        ResourceDiscovery-->>TransportDispatcher: false
        TransportDispatcher-->>Server: SerializeError("Resource disabled")
        Server-->>LLM: error response (resource disabled)
    else no resource metadata or enabled
        TransportDispatcher->>ServiceLocator: ToolDiscovery
        ServiceLocator-->>TransportDispatcher: IToolDiscoveryService
        TransportDispatcher->>ToolDiscovery: GetToolMetadata(command.type)
        ToolDiscovery-->>TransportDispatcher: ToolMetadata or null
        alt tool metadata exists and disabled
            TransportDispatcher->>ToolDiscovery: IsToolEnabled(command.type)
            ToolDiscovery-->>TransportDispatcher: false
            TransportDispatcher-->>Server: SerializeError("Tool disabled")
            Server-->>LLM: error response (tool disabled)
        else no tool metadata or enabled
            TransportDispatcher->>TransportDispatcher: CommandRegistry.ExecuteCommand()
            TransportDispatcher-->>Server: command result
            Server-->>LLM: success response
        end
    end
Loading

Sequence diagram for Python resource service parsing Unity responses

sequenceDiagram
    participant Client as LLMClient
    participant ResourceService as PythonResourceService
    participant UnityBridge as UnityConnection
    participant UnityEditor
    participant ParseResource as parse_resource_response
    participant MCPModel as MCPResponseSubclass

    Client->>ResourceService: call get_layers or other resource
    ResourceService->>UnityBridge: send Unity command
    UnityBridge->>UnityEditor: execute resource command
    UnityEditor-->>UnityBridge: response dict or error shape
    UnityBridge-->>ResourceService: raw response

    ResourceService->>ParseResource: parse_resource_response(response, TypedResponse)
    alt response is error dict
        ParseResource-->>ResourceService: MCPResponse(success=False, error, message)
    else response is success dict
        ParseResource-->>ResourceService: TypedResponse(**response)
    end

    ResourceService-->>Client: MCPResponse or TypedResponse
Loading

Class diagram for new resource discovery and editor integration

classDiagram
    class MCPForUnityEditorWindow {
        - McpResourcesSection resourcesSection
        - ToolbarToggle resourcesTabToggle
        - VisualElement resourcesPanel
        - bool resourcesLoaded
        + void CreateGUI()
        - void EnsureResourcesLoaded()
        - void SetupTabs()
        - void SwitchPanel(ActivePanel panel)
    }

    class McpResourcesSection {
        - Dictionary~string, Toggle~ resourceToggleMap
        - Label summaryLabel
        - Label noteLabel
        - Button enableAllButton
        - Button disableAllButton
        - Button rescanButton
        - VisualElement categoryContainer
        - List~ResourceMetadata~ allResources
        + VisualElement Root
        + McpResourcesSection(VisualElement root)
        - void CacheUIElements()
        - void RegisterCallbacks()
        + void Refresh()
        - void BuildCategory(string title, string prefsSuffix, IEnumerable~ResourceMetadata~ resources)
        - VisualElement CreateResourceRow(ResourceMetadata resource)
        - void HandleToggleChange(ResourceMetadata resource, bool enabled, bool updateSummary)
        - void SetAllResourcesState(bool enabled)
        - void UpdateSummary()
        - void AddInfoLabel(string message)
        - static Label CreateTag(string text)
    }

    class IResourceDiscoveryService {
        <<interface>>
        + List~ResourceMetadata~ DiscoverAllResources()
        + ResourceMetadata GetResourceMetadata(string resourceName)
        + List~ResourceMetadata~ GetEnabledResources()
        + bool IsResourceEnabled(string resourceName)
        + void SetResourceEnabled(string resourceName, bool enabled)
        + void InvalidateCache()
    }

    class ResourceDiscoveryService {
        - Dictionary~string, ResourceMetadata~ _cachedResources
        + List~ResourceMetadata~ DiscoverAllResources()
        + ResourceMetadata GetResourceMetadata(string resourceName)
        + List~ResourceMetadata~ GetEnabledResources()
        + bool IsResourceEnabled(string resourceName)
        + void SetResourceEnabled(string resourceName, bool enabled)
        + void InvalidateCache()
        - ResourceMetadata ExtractResourceMetadata(Type type, McpForUnityResourceAttribute resourceAttr)
        - void EnsurePreferenceInitialized(ResourceMetadata metadata)
        - static string GetResourcePreferenceKey(string resourceName)
    }

    class ResourceMetadata {
        + string Name
        + string Description
        + string ClassName
        + string Namespace
        + string AssemblyName
        + bool IsBuiltIn
    }

    class MCPServiceLocator {
        - IResourceDiscoveryService _resourceDiscoveryService
        + IResourceDiscoveryService ResourceDiscovery
        + void Register~T~(T implementation)
        + void Reset()
    }

    class McpForUnityResourceAttribute {
        + string ResourceName
        + string Description
    }

    class StringCaseUtility {
        <<static>>
        + bool IsBuiltInMcpType(Type type, string assemblyName, string builtInNamespacePrefix)
        + string ToSnakeCase(string input)
    }

    class ToolDiscoveryService {
        + List~ToolMetadata~ DiscoverAllTools()
        - ToolMetadata ExtractToolMetadata(Type type, McpForUnityToolAttribute toolAttr)
    }

    MCPForUnityEditorWindow --> McpResourcesSection : owns
    MCPForUnityEditorWindow --> MCPServiceLocator : uses
    McpResourcesSection --> MCPServiceLocator : uses
    MCPServiceLocator --> IResourceDiscoveryService : exposes
    ResourceDiscoveryService ..|> IResourceDiscoveryService
    ResourceDiscoveryService --> ResourceMetadata : creates
    ResourceDiscoveryService --> StringCaseUtility : uses
    ResourceDiscoveryService --> McpForUnityResourceAttribute : reads
    McpResourcesSection --> ResourceMetadata : displays
    ToolDiscoveryService --> StringCaseUtility : uses
    McpForUnityResourceAttribute <.. ResourceMetadata
Loading

File-Level Changes

Change Details Files
Add Resources tab to the MCP for Unity editor window with lazy-loaded content.
  • Introduce resources tab toggle, panel, and container wiring in MCPForUnityEditorWindow, including enum extension, panel switching logic, and state persistence via EditorPrefs
  • Load McpResourcesSection UXML and controller on GUI creation and lazily refresh the section when its tab is activated
  • Track resourcesLoaded lifecycle alongside toolsLoaded and reset it when the window is disabled
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.uxml
MCPForUnity/Editor/Windows/Components/Resources/McpResourcesSection.cs
MCPForUnity/Editor/Windows/Components/Resources/McpResourcesSection.uxml
MCPForUnity/Editor/Windows/Components/Resources.meta
MCPForUnity/Editor/Windows/Components/Resources/McpResourcesSection.cs.meta
MCPForUnity/Editor/Windows/Components/Resources/McpResourcesSection.uxml.meta
Implement reflection-based resource discovery and enable/disable state management.
  • Define IResourceDiscoveryService and ResourceMetadata to describe MCP resources and access discovery APIs
  • Implement ResourceDiscoveryService using TypeCache to find McpForUnityResourceAttribute types, extract metadata, and cache results
  • Persist per-resource enabled state in EditorPrefs via new resource-specific keys and default newly discovered resources to enabled
  • Register ResourceDiscovery in MCPServiceLocator and ensure proper disposal/reset on locator reset
  • Extend MCP resources attribute with a Description field to provide human-readable metadata
MCPForUnity/Editor/Services/IResourceDiscoveryService.cs
MCPForUnity/Editor/Services/IResourceDiscoveryService.cs.meta
MCPForUnity/Editor/Services/ResourceDiscoveryService.cs
MCPForUnity/Editor/Services/ResourceDiscoveryService.cs.meta
MCPForUnity/Editor/Resources/McpForUnityResourceAttribute.cs
MCPForUnity/Editor/Constants/EditorPrefKeys.cs
MCPForUnity/Editor/Services/MCPServiceLocator.cs
Wire the Resources section UI to discovery service and provide per-resource controls.
  • Create McpResourcesSection controller that builds categorized lists of built-in vs custom resources with foldouts and tags
  • Bind enable/disable toggles to ResourceDiscovery IsResourceEnabled/SetResourceEnabled and support bulk enable/disable as well as rescan
  • Display summary and helper messaging when no resources are found and keep an up-to-date enabled/total count
MCPForUnity/Editor/Windows/Components/Resources/McpResourcesSection.cs
MCPForUnity/Editor/Windows/Components/Resources/McpResourcesSection.uxml
Block disabled tools and resources at transport dispatch time and surface clear errors back to the LLM.
  • Before executing a command, look up resource metadata and short-circuit with a serialized error if the resource is disabled
  • Apply the same short-circuit behavior for tools by checking ToolDiscovery IsToolEnabled
  • Use MCPServiceLocator to reach both discovery services and ensure pending commands are cleaned up when blocked
MCPForUnity/Editor/Services/Transport/TransportCommandDispatcher.cs
MCPForUnity/Editor/Services/MCPServiceLocator.cs
Centralize built-in type detection and reuse it for tools and resources.
  • Introduce StringCaseUtility.IsBuiltInMcpType to determine whether a type belongs to the built-in MCP for Unity package based on namespace prefix or assembly name
  • Refactor ToolDiscoveryService to use the new helper instead of local DetermineIsBuiltIn and remove the redundant private method
  • Use the helper in ResourceDiscoveryService to mark built-in resources via a resources-specific namespace prefix
MCPForUnity/Editor/Helpers/StringCaseUtility.cs
MCPForUnity/Editor/Services/ToolDiscoveryService.cs
MCPForUnity/Editor/Services/ResourceDiscoveryService.cs
Improve Python server-side handling of Unity resource responses to avoid validation errors on failures.
  • Add parse_resource_response helper that returns a base MCPResponse for error-shaped payloads and otherwise instantiates a typed response class
  • Export parse_resource_response from the models package so resource services can consume it
  • Update all resource service functions to use parse_resource_response when wrapping normalize_unity_response output, including tests and active tool/windows/layers/etc. endpoints
Server/src/models/unity_response.py
Server/src/models/__init__.py
Server/src/services/resources/tests.py
Server/src/services/resources/active_tool.py
Server/src/services/resources/layers.py
Server/src/services/resources/menu_items.py
Server/src/services/resources/prefab_stage.py
Server/src/services/resources/project_info.py
Server/src/services/resources/selection.py
Server/src/services/resources/tags.py
Server/src/services/resources/windows.py

Assessment against linked issues

Issue Objective Addressed Explanation
#648 Add a Resources tab/section in the MCP for Unity editor window that lists all MCP resources, similar to the existing Tools tab.
#648 Allow enabling/disabling individual resources (with persistence via editor preferences) and manage them via a discovery service analogous to tools.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

Adds resource discovery and metadata types, a ResourceDiscovery service registered in MCPServiceLocator, EditorPrefs-backed enable/disable toggles, a Resources editor UI/tab with foldouts and toggles, pre-execution guards for disabled resources/tools, and centralized server-side resource response parsing. (50 words)

Changes

Cohort / File(s) Summary
Constants & Utilities
MCPForUnity/Editor/Constants/EditorPrefKeys.cs, MCPForUnity/Editor/Helpers/StringCaseUtility.cs
Added two EditorPrefs key prefixes for resources and a new helper to detect built-in MCP types by namespace or assembly.
Resource Attribute
MCPForUnity/Editor/Resources/McpForUnityResourceAttribute.cs
Exposed a Description property on the resource attribute.
Resource Discovery Service
MCPForUnity/Editor/Services/IResourceDiscoveryService.cs, MCPForUnity/Editor/Services/ResourceDiscoveryService.cs, .../ResourceDiscoveryService.cs.meta
New interface and implementation that discover resources via reflection, build/caches ResourceMetadata, persist per-resource enablement in EditorPrefs, and expose query/mutation APIs plus cache invalidation.
Service Locator Integration
MCPForUnity/Editor/Services/MCPServiceLocator.cs
Registered ResourceDiscovery with lazy init, Register handling, and Reset cleanup.
Discovery Refactor
MCPForUnity/Editor/Services/ToolDiscoveryService.cs
Replaced inline built-in detection with the new StringCaseUtility helper and removed the old helper.
Command Dispatch Guards
MCPForUnity/Editor/Services/Transport/TransportCommandDispatcher.cs
Added pre-execution checks to abort processing for commands targeting disabled resources or tools, returning an error payload and cleaning pending state.
Editor UI: Resources
MCPForUnity/Editor/Windows/Components/Resources/...
Added UXML, VisualElement controller (McpResourcesSection), and meta files to render resource categories, foldouts, enable/disable toggles, action buttons (Enable All / Disable All / Rescan), and persist foldout state.
Editor Window Integration
MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs, .../MCPForUnityEditorWindow.uxml
Added Resources tab/panel, lazy-loading EnsureResourcesLoaded(), tab wiring, resourcesLoaded state, and panel switching logic.
Server-side Parsing
Server/src/models/unity_response.py, Server/src/models/__init__.py, Server/src/services/resources/...
Added parse_resource_response and updated multiple resource service handlers to use centralized parsing instead of inline dict-to-model construction.
Misc Unity meta files
.../*.meta, .../*.uxml
New Unity .meta and UXML assets added for the Resources UI components.

Sequence Diagram(s)

sequenceDiagram
    actor User as Editor User
    participant Window as MCPForUnityEditorWindow
    participant ResourceSection as McpResourcesSection
    participant Locator as MCPServiceLocator
    participant Discovery as ResourceDiscoveryService
    participant Reflection as TypeCache/Reflection
    participant EditorPrefs as EditorPrefs

    User->>Window: Click Resources tab
    Window->>Window: EnsureResourcesLoaded()
    Window->>ResourceSection: Create/Refresh UI
    ResourceSection->>Locator: ResourceDiscovery.DiscoverAllResources()
    Locator->>Discovery: DiscoverAllResources()
    Discovery->>Reflection: Scan types for MCPForUnityResourceAttribute
    Reflection-->>Discovery: Types list
    loop each discovered type
        Discovery->>Discovery: ExtractResourceMetadata(type, attribute)
        Discovery->>EditorPrefs: EnsurePreferenceInitialized(resourceName)
    end
    Discovery-->>Locator: List<ResourceMetadata>
    Locator-->>ResourceSection: List<ResourceMetadata>
    ResourceSection->>ResourceSection: Build UI with toggles/foldouts
    User->>ResourceSection: Toggle resource
    ResourceSection->>Discovery: SetResourceEnabled(name, enabled)
    Discovery->>EditorPrefs: Persist enabled state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • justinpbarnett
  • dsarno
  • Scriptwonder

Poem

🐰 I hopped through code to find each resource name,
I sniffed their namespaces and saved their fame.
Toggles remembered in EditorPrefs' light,
Built-in or custom, I guard each command's flight.
Hop! The Resources tab now dances into sight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.61% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Display resources' directly reflects the main feature added - a new Resources tab in the editor UI. It is concise, clear, and conveys the primary change from the developer's perspective.
Description check ✅ Passed The PR description includes all key sections: Description, Type of Change (marked as both bug fix and new feature), Changes Made, Testing/Screenshots, Documentation Updates checkbox, Related Issues, and Additional Notes with a detailed Sourcery summary.
Linked Issues check ✅ Passed The PR fully implements the objective from #648 - it adds resource discovery, categorization, per-resource enable/disable controls, metadata caching, and displays resources in a dedicated UI tab, enabling users to manage resources similarly to tools.
Out of Scope Changes check ✅ Passed All changes are scoped to the linked objective #648. The refactoring of built-in type detection into a shared utility, response parsing helpers on the server, and the fix for disabled resource/tool execution all directly support the resource management and display feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In parse_resource_response, consider always returning an MCPResponse (e.g., wrapping non-dict responses as an error) so the behavior matches the return type annotation and callers don’t have to handle raw values.
  • In ResourceDiscoveryService, resources are keyed solely by resourceName, so duplicate names will silently override each other; it may be safer to detect and log a warning or include namespace/type in the key to avoid accidental collisions.
  • The built-in type detection in StringCaseUtility.IsBuiltInMcpType hardcodes the assembly name string; consider deriving this from a known type (e.g., typeof(SomeEditorType).Assembly.GetName().Name) or centralizing the constant to avoid drift if the assembly name changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `parse_resource_response`, consider always returning an `MCPResponse` (e.g., wrapping non-dict responses as an error) so the behavior matches the return type annotation and callers don’t have to handle raw values.
- In `ResourceDiscoveryService`, resources are keyed solely by `resourceName`, so duplicate names will silently override each other; it may be safer to detect and log a warning or include namespace/type in the key to avoid accidental collisions.
- The built-in type detection in `StringCaseUtility.IsBuiltInMcpType` hardcodes the assembly name string; consider deriving this from a known type (e.g., `typeof(SomeEditorType).Assembly.GetName().Name`) or centralizing the constant to avoid drift if the assembly name changes.

## Individual Comments

### Comment 1
<location> `MCPForUnity/Editor/Services/ResourceDiscoveryService.cs:23-32` </location>
<code_context>
+            _cachedResources = new Dictionary<string, ResourceMetadata>();
</code_context>

<issue_to_address>
**issue (bug_risk):** Handle duplicate resource names so later discoveries don’t silently overwrite earlier ones.

Keying `Dictionary<string, ResourceMetadata>` by `metadata.Name` will silently overwrite colliding resource names; add collision detection with logging or explicit precedence to surface misconfigurations.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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: 2

🤖 Fix all issues with AI agents
In `@MCPForUnity/Editor/Services/ResourceDiscoveryService.cs`:
- Around line 56-64: GetResourceMetadata can pass a null/empty key to
_cachedResources.TryGetValue which throws; add a defensive guard at the start of
GetResourceMetadata to return null if resourceName is null or empty, ensure
_cachedResources is populated by calling DiscoverAllResources() as currently
done, and then safely use _cachedResources.TryGetValue(resourceName, out var
metadata) to return metadata or null; reference GetResourceMetadata,
_cachedResources, and DiscoverAllResources.
- Around line 44-49: When discovering resources, guard against duplicate
resource names so earlier entries aren't silently overwritten: after calling
ExtractResourceMetadata(type, resourceAttr) and before assigning to
_cachedResources[metadata.Name], check whether _cachedResources already contains
metadata.Name; if it does, log a warning (including the conflicting type names
and metadata.Name) and skip adding the new metadata (or alternatively
throw/require explicit naming), otherwise add and call
EnsurePreferenceInitialized(metadata); reference the ExtractResourceMetadata
call, the _cachedResources dictionary, metadata.Name, and
EnsurePreferenceInitialized to locate and implement the guard.
🧹 Nitpick comments (2)
Server/src/models/unity_response.py (1)

52-70: Consider handling non-dict responses more explicitly.

When response is not a dict (lines 59-60), the function returns it directly. However, the return type annotation is MCPResponse, which could cause type confusion if the caller receives something unexpected (e.g., None, a string, or another primitive).

This may be intentional as a pass-through for already-parsed responses, but if so, the return type should be Any or the docstring should clarify this behavior.

💡 Option: Return an error MCPResponse for unexpected input types
 def parse_resource_response(response: Any, typed_cls: Type[MCPResponse]) -> MCPResponse:
     """Parse a Unity response into a typed response class.
 
     Returns a base ``MCPResponse`` for error responses so that typed subclasses
     with strict ``data`` fields (e.g. ``list[str]``) don't raise Pydantic
     validation errors when ``data`` is ``None``.
     """
     if not isinstance(response, dict):
-        return response
+        return MCPResponse(
+            success=False,
+            error=f"Expected dict response, got {type(response).__name__}",
+        )
 
     # Detect errors from both normalized (success=False) and raw (status="error") shapes.
     if response.get("success") is False or response.get("status") == "error":
MCPForUnity/Editor/Windows/Components/Resources/McpResourcesSection.cs (1)

18-81: Consider centralizing required UI element validation to avoid silent no-ops.
The current null-conditional checks can mask UXML mismatches, leaving the Resources tab empty without a clear error. A single “UI ready” validation keeps the flow fail-fast and avoids repeated null checks.

♻️ Proposed refactor (centralize validation and short-circuit)
@@
-        private List<ResourceMetadata> allResources = new();
+        private List<ResourceMetadata> allResources = new();
+        private bool uiReady;
@@
         private void CacheUIElements()
         {
             summaryLabel = Root.Q<Label>("resources-summary");
             noteLabel = Root.Q<Label>("resources-note");
             enableAllButton = Root.Q<Button>("enable-all-resources-button");
             disableAllButton = Root.Q<Button>("disable-all-resources-button");
             rescanButton = Root.Q<Button>("rescan-resources-button");
             categoryContainer = Root.Q<VisualElement>("resource-category-container");
+
+            uiReady = summaryLabel != null && noteLabel != null
+                && enableAllButton != null && disableAllButton != null
+                && rescanButton != null && categoryContainer != null;
+            if (!uiReady)
+            {
+                McpLog.Error("Resources UXML is missing required elements.");
+            }
         }
@@
         private void RegisterCallbacks()
         {
-            if (enableAllButton != null)
-            {
-                enableAllButton.AddToClassList("tool-action-button");
-                enableAllButton.style.marginRight = 4;
-                enableAllButton.clicked += () => SetAllResourcesState(true);
-            }
-
-            if (disableAllButton != null)
-            {
-                disableAllButton.AddToClassList("tool-action-button");
-                disableAllButton.style.marginRight = 4;
-                disableAllButton.clicked += () => SetAllResourcesState(false);
-            }
-
-            if (rescanButton != null)
-            {
-                rescanButton.AddToClassList("tool-action-button");
-                rescanButton.clicked += () =>
-                {
-                    McpLog.Info("Rescanning MCP resources from the editor window.");
-                    MCPServiceLocator.ResourceDiscovery.InvalidateCache();
-                    Refresh();
-                };
-            }
+            if (!uiReady)
+            {
+                return;
+            }
+
+            enableAllButton.AddToClassList("tool-action-button");
+            enableAllButton.style.marginRight = 4;
+            enableAllButton.clicked += () => SetAllResourcesState(true);
+
+            disableAllButton.AddToClassList("tool-action-button");
+            disableAllButton.style.marginRight = 4;
+            disableAllButton.clicked += () => SetAllResourcesState(false);
+
+            rescanButton.AddToClassList("tool-action-button");
+            rescanButton.clicked += () =>
+            {
+                McpLog.Info("Rescanning MCP resources from the editor window.");
+                MCPServiceLocator.ResourceDiscovery.InvalidateCache();
+                Refresh();
+            };
         }
@@
         public void Refresh()
         {
+            if (!uiReady)
+            {
+                return;
+            }
             resourceToggleMap.Clear();
-            categoryContainer?.Clear();
+            categoryContainer.Clear();

Based on learnings: In MCPForUnity, prefer relying on the established testing process to catch UI initialization issues instead of adding defensive null checks for UI elements in editor windows.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@msanatan
Copy link
Member Author

This might be the least amount of comments I've received from Sourcery and CodeRabbit. My goodness does this feel good!

@msanatan msanatan merged commit 61284cc into CoplayDev:beta Jan 31, 2026
2 checks passed
@msanatan msanatan deleted the display-resources branch January 31, 2026 00:31
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.

List resources in the menu

1 participant