feat: add search_missing_references tool#1018
feat: add search_missing_references tool#1018zaferdace wants to merge 2 commits intoCoplayDev:betafrom
Conversation
…oss scene and project Add a new standalone tool that scans for missing scripts, broken object references, and broken prefab links at the serialized property level. Supports two scopes: - scene: scans active scene hierarchy - project: scans all prefabs, ScriptableObjects, and materials via AssetDatabase Features: - Per-property broken reference detection via SerializedObject iteration - Missing script detection with optional auto-repair (Undo supported) - Broken prefab instance detection - Path and component type filtering - Paginated results with 5000 issue detail cap - Consistent with existing manage_scene validate but significantly more detailed Includes Python tool, C# handler, and 10 unit tests.
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR introduces a new MCP tool Changes
Sequence DiagramsequenceDiagram
participant Client
participant PythonServer as Python Server<br/>(search_missing_references)
participant Preflight
participant UnityEditor as Unity Editor<br/>(SearchMissingReferences)
participant Assets as Project Assets<br/>& Scenes
Client->>PythonServer: search_missing_references(scope, filters, page_size, cursor)
PythonServer->>PythonServer: Validate & coerce parameters
PythonServer->>Preflight: preflight(wait_for_no_compile=True)
alt Compilation in progress
Preflight-->>PythonServer: Return early
else Ready to scan
Preflight-->>PythonServer: Continue
PythonServer->>UnityEditor: send_command("search_missing_references")<br/>with validated params
alt Scope = "scene"
UnityEditor->>Assets: Traverse active scene GameObjects
else Scope = "project"
UnityEditor->>Assets: Scan prefab/asset serialized contents
end
Assets-->>UnityEditor: Issue records<br/>(missing scripts, broken refs, broken prefabs)
UnityEditor->>UnityEditor: Paginate results<br/>per page_size & cursor
alt autoRepair enabled
UnityEditor->>Assets: Remove missing MonoBehaviours
end
UnityEditor-->>PythonServer: SuccessResponse(totals, details, page data)
end
PythonServer->>PythonServer: Normalize response shape
PythonServer-->>Client: {success, message, data}
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment Tip You can get early access to new features in CodeRabbit.Enable the |
Reviewer's GuideImplements a new cross-language search_missing_references tool that coordinates between Python and Unity editor code to scan scenes and project assets for missing scripts, broken object references, and broken prefab links with filtering and pagination, plus tests to validate parameter forwarding and omission behavior. Sequence diagram for the search_missing_references end-to-end tool flowsequenceDiagram
actor Caller
participant PythonTool as search_missing_references_py
participant Preflight as preflight
participant UnityTransport as send_with_unity_instance
participant UnityEditor as SearchMissingReferences_cs
Caller->>PythonTool: invoke search_missing_references(scope, filters, pagination)
PythonTool->>PythonTool: coerce_bool / coerce_int on parameters
PythonTool->>Preflight: preflight(wait_for_no_compile, refresh_if_dirty)
Preflight-->>PythonTool: gate (None or error)
alt preflight blocked
PythonTool-->>Caller: gate.model_dump()
else preflight ok
PythonTool->>UnityTransport: send_with_unity_instance(async_send_command_with_retry, unity_instance, search_missing_references, camelCase params)
UnityTransport->>UnityEditor: HandleCommand(JObject params)
UnityEditor->>UnityEditor: ToolParams + PaginationRequest.FromParams
UnityEditor->>UnityEditor: clamp PageSize [1,500], normalize Cursor
UnityEditor->>UnityEditor: read scope, filters, include flags, autoRepair
alt scope == scene
UnityEditor->>UnityEditor: SceneManager.GetActiveScene()
UnityEditor->>UnityEditor: iterate root GameObjects and Transforms
UnityEditor->>UnityEditor: ScanGameObject for each Transform
else scope == project
UnityEditor->>UnityEditor: AssetDatabase.FindAssets(prefabs, SOs, materials)
UnityEditor->>UnityEditor: load each asset on demand
UnityEditor->>UnityEditor: ScanGameObject / ScanSerializedObject
end
UnityEditor->>UnityEditor: paginate issues with PaginationRequest
UnityEditor-->>UnityTransport: SuccessResponse or ErrorResponse
UnityTransport-->>PythonTool: response dict
alt response.success
PythonTool-->>Caller: { success: True, message, data }
else error or non dict
PythonTool-->>Caller: { success: False, message }
end
end
Updated class diagram for the SearchMissingReferences Unity editor toolclassDiagram
class SearchMissingReferences {
<<static>>
- const int MaxIssueDetails
+ static object HandleCommand(JObject params)
- static void ScanGameObject(GameObject go, string path, bool includeMissingScripts, bool includeBrokenReferences, bool includeBrokenPrefabs, string componentFilter, bool autoRepair, List~object~ issues, ref int missingScripts, ref int brokenReferences, ref int brokenPrefabs, ref int repaired, ref bool sceneDirty, ref bool assetsDirty, bool isProjectAsset)
- static void ScanSerializedObject(Object obj, string path, string componentTypeName, bool includeBrokenReferences, string componentFilter, List~object~ issues, ref int brokenReferences, string gameObjectName)
- static string GetGameObjectPath(GameObject go)
}
class ToolParams {
+ ToolParams(JObject rawParams)
+ string Get(string key)
+ string Get(string key, string defaultValue)
+ bool GetBool(string key, bool defaultValue)
}
class PaginationRequest {
+ int PageSize
+ int Cursor
+ static PaginationRequest FromParams(JObject rawParams, int defaultPageSize)
}
class ErrorResponse {
+ ErrorResponse(string message)
}
class SuccessResponse {
+ SuccessResponse(string message, object data)
}
class McpLog {
+ static void Error(string message)
}
class UnitySceneAPI {
<<utility>>
+ static Scene GetActiveScene()
+ static GameObject[] GetRootGameObjects(Scene scene)
}
class UnityAssetAPI {
<<utility>>
+ static string[] FindAssets(string filter, string[] searchInFolders)
+ static string GUIDToAssetPath(string guid)
+ static GameObject LoadAssetAtPathGameObject(string path)
+ static ScriptableObject LoadAssetAtPathScriptableObject(string path)
+ static Material LoadAssetAtPathMaterial(string path)
+ static void SaveAssets()
}
class UnityPrefabAPI {
<<utility>>
+ static PrefabInstanceStatus GetPrefabInstanceStatus(GameObject go)
}
class UnityGameObjectUtility {
<<utility>>
+ static int GetMonoBehavioursWithMissingScriptCount(GameObject go)
+ static int RemoveMonoBehavioursWithMissingScript(GameObject go)
}
class UnityUndoAPI {
<<utility>>
+ static void RegisterCompleteObjectUndo(Object target, string name)
}
class UnityEditorUtilityAPI {
<<utility>>
+ static void SetDirty(Object target)
}
class UnityEditorSceneAPI {
<<utility>>
+ static void MarkSceneDirty(Scene scene)
}
SearchMissingReferences --> ToolParams : uses
SearchMissingReferences --> PaginationRequest : configures pagination
SearchMissingReferences --> ErrorResponse : returns on error
SearchMissingReferences --> SuccessResponse : returns on success
SearchMissingReferences --> McpLog : logs errors
SearchMissingReferences --> UnitySceneAPI : scans scene hierarchy
SearchMissingReferences --> UnityAssetAPI : scans project assets
SearchMissingReferences --> UnityPrefabAPI : detects broken prefabs
SearchMissingReferences --> UnityGameObjectUtility : detects/removes missing scripts
SearchMissingReferences --> UnityUndoAPI : wraps auto repair in Undo
SearchMissingReferences --> UnityEditorUtilityAPI : marks assets dirty
SearchMissingReferences --> UnityEditorSceneAPI : marks scene dirty
Flow diagram for SearchMissingReferences.HandleCommand scene vs project scanning and paginationflowchart TD
A[Start HandleCommand] --> B{params is null?}
B -->|yes| C[Return ErrorResponse Parameters cannot be null]
B -->|no| D[Create ToolParams and PaginationRequest]
D --> E[Clamp PageSize to 1..500 and normalize Cursor]
E --> F[Read scope, filters, include flags, autoRepair]
F --> G{scope == scene?}
G -->|yes| H[Get active scene]
H --> I{scene valid and loaded?}
I -->|no| J[Return ErrorResponse No active scene found]
I -->|yes| K[Iterate root GameObjects and child Transforms]
K --> L[Call ScanGameObject for each Transform]
L --> M{sceneDirty?}
M -->|yes| N[MarkSceneDirty]
M -->|no| O[Skip scene save]
N --> Q[Aggregate counters and issues]
O --> Q
G -->|no| P{scope == project?}
P -->|no| R[Return ErrorResponse Invalid scope]
P -->|yes| S[Compute searchInFolders from pathFilter]
S --> T[FindAssets for prefabs, ScriptableObjects, materials]
T --> U[For each GUID, load asset at path]
U --> V{asset is prefab?}
V -->|yes| W[Iterate prefab Transforms and ScanGameObject]
V -->|no| X{asset is ScriptableObject?}
X -->|yes| Y[ScanSerializedObject for ScriptableObject]
X -->|no| Z{asset is Material?}
Z -->|yes| AA[ScanSerializedObject for Material]
Z -->|no| AB[Skip asset]
W --> AC[Next asset]
Y --> AC
AA --> AC
AB --> AC
AC --> AD{assetsDirty?}
AD -->|yes| AE[AssetDatabase.SaveAssets]
AD -->|no| AF[Skip asset save]
AE --> Q
AF --> Q
Q --> AG[Compute totalIssues and totalCount]
AG --> AH[Apply pagination: cursor, pageSize, nextCursor]
AH --> AI[Build note about autoRepair behavior]
AI --> AJ[Return SuccessResponse with summary, paged issues, pagination, truncated flag]
AJ --> AK[End HandleCommand]
C --> AK
J --> AK
R --> AK
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- In
ScanGameObject, missing scripts and broken prefab checks are skipped whenever acomponentFilteris provided (hasComponentFilterguard), which means a user asking forcomponent_filter="MeshRenderer"will never see missing-script or broken-prefab issues at all; consider either documenting this behavior clearly or adjusting the logic so component filtering only applies to the per-component serialized reference scan. - For project-scope prefab results, the
pathyou return is just the asset path (e.g.Assets/Prefabs/Foo.prefab), which makes it hard to locate the specific child object with an issue; consider including the hierarchical path inside the prefab (e.g.Assets/Prefabs/Foo.prefab/FooRoot/Child) similar toGetGameObjectPathin scene scope.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `ScanGameObject`, missing scripts and broken prefab checks are skipped whenever a `componentFilter` is provided (`hasComponentFilter` guard), which means a user asking for `component_filter="MeshRenderer"` will never see missing-script or broken-prefab issues at all; consider either documenting this behavior clearly or adjusting the logic so component filtering only applies to the per-component serialized reference scan.
- For project-scope prefab results, the `path` you return is just the asset path (e.g. `Assets/Prefabs/Foo.prefab`), which makes it hard to locate the specific child object with an issue; consider including the hierarchical path inside the prefab (e.g. `Assets/Prefabs/Foo.prefab/FooRoot/Child`) similar to `GetGameObjectPath` in scene scope.
## Individual Comments
### Comment 1
<location path="MCPForUnity/Editor/Tools/SearchMissingReferences.cs" line_range="108" />
<code_context>
+ {
+ ScanGameObject(
+ transform.gameObject,
+ assetPath,
+ includeMissingScripts,
+ includeBrokenReferences,
</code_context>
<issue_to_address>
**suggestion:** For prefab assets, consider including the full GameObject path within the prefab instead of only the asset path.
Right now `path` is just the `assetPath`, so all issues from child objects within the same prefab get reported with an identical path, which makes it hard to pinpoint the exact child. Consider passing something like `assetPath + ":" + GetGameObjectPath(transform.gameObject)` so the report includes both the prefab asset and the internal hierarchy path.
Suggested implementation:
```csharp
ScanGameObject(
transform.gameObject,
assetPath + ":" + GetGameObjectPath(transform),
includeMissingScripts,
includeBrokenReferences,
includeBrokenPrefabs,
componentFilter,
autoRepair,
allIssues,
ref missingScripts,
ref brokenReferences,
ref brokenPrefabs,
ref repaired,
ref sceneDirty,
ref assetsDirty,
```
1. Add a helper method (if one does not already exist in this file or a shared utility) to compute the internal hierarchy path for a `Transform`, for example:
```csharp
private static string GetGameObjectPath(Transform transform)
{
if (transform == null)
return string.Empty;
var sb = new System.Text.StringBuilder(transform.name);
var current = transform.parent;
while (current != null)
{
sb.Insert(0, current.name + "/");
current = current.parent;
}
return sb.ToString();
}
```
2. Ensure that the `ScanGameObject` method signature matches the new call, i.e. that it accepts the `path` string as the second parameter followed by `includeMissingScripts`, `includeBrokenReferences`, `includeBrokenPrefabs`, etc. If the parameter order differs in your codebase, adjust the call accordingly to preserve the intended behavior.
</issue_to_address>
### Comment 2
<location path="Server/src/services/tools/search_missing_references.py" line_range="28-34" />
<code_context>
+ description="Scan scope: the active scene hierarchy or project assets."
+ ),
+ ] = "scene",
+ include_missing_scripts: Annotated[
+ bool | str | None,
+ Field(
+ default=None,
+ description="Check for MonoBehaviours with missing scripts."
+ ),
+ ] = None,
+ include_broken_references: Annotated[
+ bool | str | None,
</code_context>
<issue_to_address>
**suggestion:** The Python defaults and the coercion logic effectively make include_* options default to True, which isn’t obvious from the signature/docstring.
These flags are typed and documented as optional with a default of `None`, but `coerce_bool(..., default=True)` makes the *effective* default `True` when omitted. To avoid confusion, consider either changing the signature defaults to `True` (and narrowing the type to `bool | str`) or explicitly stating in the parameter docs that the effective default value is `True` when not provided.
Suggested implementation:
```python
include_missing_scripts: Annotated[
bool | str,
Field(
default=True,
description="Check for MonoBehaviours with missing scripts."
),
] = True,
```
```python
include_broken_references: Annotated[
bool | str,
Field(
default=True,
description="Check for broken object references in serialized properties."
```
These changes assume that elsewhere in this file (or the calling code) `coerce_bool` or similar logic already uses `default=True` for these flags. If any validation or coercion logic still expects `None` as a possible value, update those call sites to treat the parameters as `bool | str` with a default of `True` and remove any special handling of `None`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
MCPForUnity/Editor/Tools/SearchMissingReferences.cs (2)
380-383: Consider logging or specifying exception type in catch block.The empty
catchblock silently swallows any exception. While this is defensive for edge cases (e.g., destroyed objects during iteration), consider either:
- Catching a specific exception type, or
- Adding a debug log to aid troubleshooting if path building fails unexpectedly.
This is a minor suggestion given the fallback behavior is reasonable.
Optional: Add minimal logging
catch { + // Fallback to simple name if hierarchy traversal fails return go.name; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@MCPForUnity/Editor/Tools/SearchMissingReferences.cs` around lines 380 - 383, The current empty catch that returns go.name silently swallows errors; update that catch to either catch a specific Unity exception (e.g., MissingReferenceException or NullReferenceException) or catch Exception ex and log the error before returning. In the catch block near the code that returns go.name (in SearchMissingReferences.cs where "go" is used to build the path), call UnityEngine.Debug.LogWarning or Debug.LogException with context (include go.name and the exception message/stack) so failures are visible while preserving the fallback return.
12-13: Add Unity Editor tests for SearchMissingReferences.Per project learnings and existing test patterns in
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/, new tools should have dedicated test files. SearchMissingReferencesTests.cs would validate the scanning logic, missing script detection, broken reference detection, and auto-repair behavior—aspects that Python-side parameter tests don't cover.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@MCPForUnity/Editor/Tools/SearchMissingReferences.cs` around lines 12 - 13, Add an EditMode Unity test file named SearchMissingReferencesTests.cs under TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ that exercises the SearchMissingReferences tool: instantiate or simulate scenes/prefabs with missing MonoBehaviour scripts and broken serialized object references, call SearchMissingReferences.Scan/Run (or the public entry methods on the SearchMissingReferences class), assert that missing-script detection and broken-reference detection produce the expected results, and verify auto-repair behavior by invoking the repair path and confirming references/scripts are fixed or removed as expected; include setup/teardown to create temporary GameObjects/Assets and use UnityEditor.TestTools assertions to mirror existing test patterns in the folder.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@MCPForUnity/Editor/Tools/SearchMissingReferences.cs`:
- Around line 380-383: The current empty catch that returns go.name silently
swallows errors; update that catch to either catch a specific Unity exception
(e.g., MissingReferenceException or NullReferenceException) or catch Exception
ex and log the error before returning. In the catch block near the code that
returns go.name (in SearchMissingReferences.cs where "go" is used to build the
path), call UnityEngine.Debug.LogWarning or Debug.LogException with context
(include go.name and the exception message/stack) so failures are visible while
preserving the fallback return.
- Around line 12-13: Add an EditMode Unity test file named
SearchMissingReferencesTests.cs under
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ that exercises the
SearchMissingReferences tool: instantiate or simulate scenes/prefabs with
missing MonoBehaviour scripts and broken serialized object references, call
SearchMissingReferences.Scan/Run (or the public entry methods on the
SearchMissingReferences class), assert that missing-script detection and
broken-reference detection produce the expected results, and verify auto-repair
behavior by invoking the repair path and confirming references/scripts are fixed
or removed as expected; include setup/teardown to create temporary
GameObjects/Assets and use UnityEditor.TestTools assertions to mirror existing
test patterns in the folder.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b632304b-2f56-4002-b6b7-c7c7f6613b31
📒 Files selected for processing (3)
MCPForUnity/Editor/Tools/SearchMissingReferences.csServer/src/services/tools/search_missing_references.pyServer/tests/test_search_missing_references.py
- Remove hasComponentFilter guard from missing script and broken prefab checks — componentFilter now only applies to broken reference scan, so users filtering by component still see all missing scripts/prefabs - Include hierarchical path inside prefabs (assetPath:Root/Child) so issues in nested prefab children are easy to locate - Change include_* Python params from None to explicit True defaults with bool|str type — makes effective defaults obvious in signature
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
Server/src/services/tools/search_missing_references.py (1)
135-141: Preserve the full success payload to avoid dropping metadata.The success branch re-shapes the response to only
success/message/data. If Unity adds extra top-level fields, they are silently lost. Returning the original dict is safer and forward-compatible.♻️ Proposed fix
- if isinstance(response, dict) and response.get("success"): - return { - "success": True, - "message": response.get("message", "Missing reference search completed."), - "data": response.get("data"), - } - return response if isinstance(response, dict) else {"success": False, "message": str(response)} + if isinstance(response, dict): + if response.get("success") and "message" not in response: + return { + **response, + "message": "Missing reference search completed.", + } + return response + return {"success": False, "message": str(response)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Server/src/services/tools/search_missing_references.py` around lines 135 - 141, The current success branch destructures the response into only {"success","message","data"}, dropping any extra top-level fields; instead return the original response dict to preserve metadata — i.e., when response is a dict and response.get("success") is truthy, return response as-is rather than building a new dict; update the logic around the variable response in search_missing_references.py so successful responses are returned intact and non-dict errors still map to {"success": False, "message": str(response)}.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@Server/src/services/tools/search_missing_references.py`:
- Around line 135-141: The current success branch destructures the response into
only {"success","message","data"}, dropping any extra top-level fields; instead
return the original response dict to preserve metadata — i.e., when response is
a dict and response.get("success") is truthy, return response as-is rather than
building a new dict; update the logic around the variable response in
search_missing_references.py so successful responses are returned intact and
non-dict errors still map to {"success": False, "message": str(response)}.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1a17f1b7-354d-4fa7-9b84-73b6327ab4bc
📒 Files selected for processing (2)
MCPForUnity/Editor/Tools/SearchMissingReferences.csServer/src/services/tools/search_missing_references.py
✅ Files skipped from review due to trivial changes (1)
- MCPForUnity/Editor/Tools/SearchMissingReferences.cs
Description
Add a new
search_missing_referencestool that scans for missing scripts, broken object references, and broken prefab links at the serialized property level. This goes significantly beyond the existingmanage_scene(action="validate")by providing per-property detail, project-wide scanning, and filtering capabilities.Type of Change
Changes Made
Python (
Server/src/services/tools/search_missing_references.py)find_gameobjectsconventionsscope,include_missing_scripts,include_broken_references,include_broken_prefabs,path_filter,component_filter,auto_repair,page_size,cursorC# (
MCPForUnity/Editor/Tools/SearchMissingReferences.cs)[McpForUnityTool("search_missing_references")]withHandleCommandpatternGetComponentsInChildren<Transform>(true)AssetDatabase.FindAssetsGameObjectUtility.GetMonoBehavioursWithMissingScriptCountSerializedObject+SerializedPropertyiteration — detectsobjectReferenceValue == null && objectReferenceInstanceIDValue != 0PrefabUtility.GetPrefabInstanceStatus == MissingAssetUndo.RegisterCompleteObjectUndosupportPaginationRequestTests (
Server/tests/test_search_missing_references.py)test_manage_scene.pyconventions (monkeypatch fixture, asyncio.run, param assertions)Comparison with Existing
manage_scene validateTesting
Example Usage
Related Issues
Builds on the existing
manage_scene validatefunctionality. This is the first step toward "Build Doctor" diagnostics for Unity projects.Additional Notes
coregroup (default enabled)truncatedfield in response indicates when issue count exceeds detail cap (counters remain accurate)🤖 Generated with Claude Code
Summary by Sourcery
Add a new MCP Unity tool and Python wrapper for scanning Unity scenes and project assets for missing scripts, broken references, and broken prefab links with pagination and filtering.
New Features:
Tests:
Summary by CodeRabbit