-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix: prefab stage dirty flag, root rename, test fix, and prefab resources #627
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
dsarno
merged 1 commit into
CoplayDev:beta
from
dsarno:feature/prefab-root-rename-and-text-search
Jan 26, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| """ | ||
| MCP Resources for reading Prefab data from Unity. | ||
|
|
||
| These resources provide read-only access to: | ||
| - Prefab info by asset path (mcpforunity://prefab/{path}) | ||
| - Prefab hierarchy by asset path (mcpforunity://prefab/{path}/hierarchy) | ||
| - Currently open prefab stage (mcpforunity://editor/prefab-stage - see prefab_stage.py) | ||
| """ | ||
| from typing import Any | ||
| from urllib.parse import unquote | ||
| from pydantic import BaseModel | ||
| from fastmcp import Context | ||
|
|
||
| from models import MCPResponse | ||
| from services.registry import mcp_for_unity_resource | ||
| from services.tools import get_unity_instance_from_context | ||
| from transport.unity_transport import send_with_unity_instance | ||
| from transport.legacy.unity_connection import async_send_command_with_retry | ||
|
|
||
|
|
||
| def _normalize_response(response: dict | MCPResponse | Any) -> MCPResponse: | ||
| """Normalize Unity transport response to MCPResponse.""" | ||
| if isinstance(response, dict): | ||
| return MCPResponse(**response) | ||
| if isinstance(response, MCPResponse): | ||
| return response | ||
| # Fallback: wrap unexpected types in an error response | ||
| return MCPResponse(success=False, error=f"Unexpected response type: {type(response).__name__}") | ||
|
|
||
|
|
||
| def _decode_prefab_path(encoded_path: str) -> str: | ||
| """ | ||
| Decode a URL-encoded prefab path. | ||
| Handles paths like 'Assets%2FPrefabs%2FMyPrefab.prefab' -> 'Assets/Prefabs/MyPrefab.prefab' | ||
| """ | ||
| return unquote(encoded_path) | ||
|
|
||
|
|
||
| # ============================================================================= | ||
| # Static Helper Resource (shows in UI) | ||
| # ============================================================================= | ||
|
|
||
| @mcp_for_unity_resource( | ||
| uri="mcpforunity://prefab-api", | ||
| name="prefab_api", | ||
| description="Documentation for Prefab resources. Use manage_asset action=search filterType=Prefab to find prefabs, then access resources below." | ||
| ) | ||
| async def get_prefab_api_docs(_ctx: Context) -> MCPResponse: | ||
| """ | ||
| Returns documentation for the Prefab resource API. | ||
|
|
||
| This is a helper resource that explains how to use the parameterized | ||
| Prefab resources which require an asset path. | ||
| """ | ||
| docs = { | ||
| "overview": "Prefab resources provide read-only access to Unity prefab assets.", | ||
| "workflow": [ | ||
| "1. Use manage_asset action=search filterType=Prefab to find prefabs", | ||
| "2. Use the asset path to access detailed data via resources below", | ||
| "3. Use manage_prefabs tool for prefab stage operations (open, save, close)" | ||
| ], | ||
| "path_encoding": { | ||
| "note": "Prefab paths must be URL-encoded when used in resource URIs", | ||
| "example": "Assets/Prefabs/MyPrefab.prefab -> Assets%2FPrefabs%2FMyPrefab.prefab" | ||
| }, | ||
| "resources": { | ||
| "mcpforunity://prefab/{encoded_path}": { | ||
| "description": "Get prefab asset info (type, root name, components, variant info)", | ||
| "example": "mcpforunity://prefab/Assets%2FPrefabs%2FPlayer.prefab", | ||
| "returns": ["assetPath", "guid", "prefabType", "rootObjectName", "rootComponentTypes", "childCount", "isVariant", "parentPrefab"] | ||
| }, | ||
| "mcpforunity://prefab/{encoded_path}/hierarchy": { | ||
| "description": "Get full prefab hierarchy with nested prefab information", | ||
| "example": "mcpforunity://prefab/Assets%2FPrefabs%2FPlayer.prefab/hierarchy", | ||
| "returns": ["prefabPath", "total", "items (with name, instanceId, path, componentTypes, prefab nesting info)"] | ||
| }, | ||
| "mcpforunity://editor/prefab-stage": { | ||
| "description": "Get info about the currently open prefab stage (if any)", | ||
| "returns": ["isOpen", "assetPath", "prefabRootName", "mode", "isDirty"] | ||
| } | ||
| }, | ||
| "related_tools": { | ||
| "manage_prefabs": "Open/close prefab stages, save changes, create prefabs from GameObjects", | ||
| "manage_asset": "Search for prefab assets, get asset info", | ||
| "manage_gameobject": "Modify GameObjects in open prefab stage", | ||
| "manage_components": "Add/remove/modify components on prefab GameObjects" | ||
| } | ||
| } | ||
| return MCPResponse(success=True, data=docs) | ||
|
|
||
|
|
||
| # ============================================================================= | ||
| # Prefab Info Resource | ||
| # ============================================================================= | ||
|
|
||
| # TODO: Use these typed response classes for better type safety once | ||
| # we update the endpoints to validate response structure more strictly. | ||
|
|
||
|
|
||
| class PrefabInfoData(BaseModel): | ||
| """Data for a prefab asset.""" | ||
| assetPath: str | ||
| guid: str = "" | ||
| prefabType: str = "Regular" | ||
| rootObjectName: str = "" | ||
| rootComponentTypes: list[str] = [] | ||
| childCount: int = 0 | ||
| isVariant: bool = False | ||
| parentPrefab: str | None = None | ||
|
|
||
|
|
||
| class PrefabInfoResponse(MCPResponse): | ||
| """Response containing prefab info data.""" | ||
| data: PrefabInfoData | None = None | ||
|
|
||
|
|
||
| @mcp_for_unity_resource( | ||
| uri="mcpforunity://prefab/{encoded_path}", | ||
| name="prefab_info", | ||
| description="Get detailed information about a prefab asset by URL-encoded path. Returns prefab type, root object name, component types, child count, and variant info." | ||
| ) | ||
| async def get_prefab_info(ctx: Context, encoded_path: str) -> MCPResponse: | ||
| """Get prefab asset info by path.""" | ||
| unity_instance = get_unity_instance_from_context(ctx) | ||
|
|
||
| # Decode the URL-encoded path | ||
| decoded_path = _decode_prefab_path(encoded_path) | ||
|
|
||
| response = await send_with_unity_instance( | ||
| async_send_command_with_retry, | ||
| unity_instance, | ||
| "manage_prefabs", | ||
| { | ||
| "action": "get_info", | ||
| "prefabPath": decoded_path | ||
| } | ||
| ) | ||
|
|
||
| return _normalize_response(response) | ||
|
|
||
|
|
||
| # ============================================================================= | ||
| # Prefab Hierarchy Resource | ||
| # ============================================================================= | ||
|
|
||
| class PrefabHierarchyItem(BaseModel): | ||
| """Single item in prefab hierarchy.""" | ||
| name: str | ||
| instanceId: int | ||
| path: str | ||
| activeSelf: bool = True | ||
| childCount: int = 0 | ||
| componentTypes: list[str] = [] | ||
| prefab: dict[str, Any] = {} | ||
|
|
||
|
|
||
| class PrefabHierarchyData(BaseModel): | ||
| """Data for prefab hierarchy.""" | ||
| prefabPath: str | ||
| total: int = 0 | ||
| items: list[PrefabHierarchyItem] = [] | ||
|
|
||
|
|
||
| class PrefabHierarchyResponse(MCPResponse): | ||
| """Response containing prefab hierarchy data.""" | ||
| data: PrefabHierarchyData | None = None | ||
|
|
||
|
|
||
| @mcp_for_unity_resource( | ||
| uri="mcpforunity://prefab/{encoded_path}/hierarchy", | ||
| name="prefab_hierarchy", | ||
| description="Get the full hierarchy of a prefab with nested prefab information. Returns all GameObjects with their components and nesting depth." | ||
| ) | ||
| async def get_prefab_hierarchy(ctx: Context, encoded_path: str) -> MCPResponse: | ||
| """Get prefab hierarchy by path.""" | ||
| unity_instance = get_unity_instance_from_context(ctx) | ||
|
|
||
| # Decode the URL-encoded path | ||
| decoded_path = _decode_prefab_path(encoded_path) | ||
|
|
||
| response = await send_with_unity_instance( | ||
| async_send_command_with_retry, | ||
| unity_instance, | ||
| "manage_prefabs", | ||
| { | ||
| "action": "get_hierarchy", | ||
| "prefabPath": decoded_path | ||
| } | ||
| ) | ||
|
|
||
| return _normalize_response(response) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.