Skip to content

[Bug]: CommandRegistry.AutoDiscoverCommands adds ~9s to every domain reload in large projects #1336

Description

@kogangdon

What happened?

On a large commercial Unity project (several hundred assemblies), every domain reload spends ~8.9 s inside CommandRegistry.AutoDiscoverCommands() — roughly half of our total ~20 s reload time. Since the registry re-initializes after every reload, we pay this cost on every script compile and every Play Mode entry.

The cause is that discovery enumerates every type of every loaded assembly (UnityAssembliesCompat.GetLoadedAssemblies()GetTypes()) and then checks GetCustomAttribute<T>() per type, twice (tools + resources). The cost scales with the project's total type count, not with the number of MCP tools.

ToolDiscoveryService.DiscoverAllTools() has the same issue: it runs a TypeCache primary scan but then unconditionally runs the exhaustive AppDomain scan as a fallback and merges the results, so the full-domain scan cost is paid there as well.

Expected: discovery should use UnityEditor.TypeCache.GetTypesWithAttribute<T>(), which is a prebuilt-index lookup and effectively free. The codebase already trusts TypeCache for exactly this pattern — ResourceDiscoveryService is TypeCache-only, and McpClientRegistry, GetMenuItems, and the graphics helpers all use TypeCache. CommandRegistry (and the ToolDiscoveryService fallback) are the remaining exhaustive scans.

Side benefit: TypeCache reads a prebuilt native index instead of reflecting over assembly metadata, which avoids the half-loaded-metadata crash class that led to the AssetImportWorker workaround in #1134.

Regarding the existing comment in ToolDiscoveryService ("TypeCache … can miss project assemblies in some domain-reload states"): if there is a known repro for that state we would love details. Otherwise a conservative fix is TypeCache primary + AppDomain fallback only when TypeCache returns nothing — same coverage guarantees, none of the per-reload cost.

Happy to submit a PR (against beta) for both call sites either way — just let us know which shape you prefer (TypeCache-only vs. lazy fallback).

Reproduction steps

  1. Install com.coplaydev.unity-mcp 10.1.0 in a project with a large number of assemblies/types (we see it with several hundred asmdefs; smaller projects pay proportionally less).
  2. Trigger a domain reload (edit any script and recompile, or enter Play Mode).
  3. Profile the reload — CommandRegistry.AutoDiscoverCommands accounts for ~8.9 s of it.
  4. Replacing the scan with TypeCache.GetTypesWithAttribute<McpForUnityToolAttribute>() / TypeCache.GetTypesWithAttribute<McpForUnityResourceAttribute>() finds the same tools/resources with discovery time effectively at 0.

Unity version

2021.3.56f2

MCP for Unity package version

10.1.0 (OpenUPM)

Python server version

n/a — editor-side issue; the Python server is not involved.

MCP client

Claude Code

Transport

stdio (the scan runs on every domain reload regardless of transport)

OS

Windows 11

Relevant logs / console output

Editor profiling of a domain reload shows ~8.9 s attributed to CommandRegistry.AutoDiscoverCommands() out of a ~20 s total reload. Happy to attach profiler captures if useful.

Checks

  • I searched existing issues and did not find a duplicate
  • I included logs / steps to reproduce

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions