Skip to content

Commit 8d0edc8

Browse files
committed
feat(prefab): implement CreatePrefabTool and clean up obsolete assets
1 parent 4d38550 commit 8d0edc8

9 files changed

+122
-519
lines changed

CREATE_PREFAB_TOOL_IMPLEMENTATION_SUMMARY.md

Lines changed: 0 additions & 107 deletions
This file was deleted.

Editor/Tools/CreatePrefabTool.cs

Lines changed: 114 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -38,153 +38,134 @@ public override JObject Execute(JObject parameters)
3838
);
3939
}
4040

41-
try
41+
// Create a temporary GameObject
42+
GameObject tempObject = new GameObject(prefabName);
43+
44+
// Add component if provided
45+
if (!string.IsNullOrEmpty(scriptName))
4246
{
43-
// Create a temporary GameObject
44-
GameObject tempObject = new GameObject(prefabName);
45-
Component component = null;
46-
47-
// Add script component if scriptName is provided
48-
if (!string.IsNullOrEmpty(scriptName))
47+
try
4948
{
50-
// Find the script type
51-
Type scriptType = Type.GetType($"{scriptName}, Assembly-CSharp");
52-
if (scriptType == null)
53-
{
54-
// Try with just the class name
55-
scriptType = Type.GetType(scriptName);
56-
}
57-
58-
if (scriptType == null)
59-
{
60-
// Try to find the type using AppDomain
61-
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
62-
{
63-
scriptType = assembly.GetType(scriptName);
64-
if (scriptType != null)
65-
break;
66-
}
67-
}
68-
69-
if (scriptType == null)
70-
{
71-
return McpUnitySocketHandler.CreateErrorResponse(
72-
$"Script type '{scriptName}' not found. Ensure the script is compiled.",
73-
"not_found_error"
74-
);
75-
}
76-
77-
// Check if the type is a MonoBehaviour
78-
if (!typeof(MonoBehaviour).IsAssignableFrom(scriptType))
79-
{
80-
return McpUnitySocketHandler.CreateErrorResponse(
81-
$"Type '{scriptName}' is not a MonoBehaviour",
82-
"invalid_type_error"
83-
);
84-
}
85-
86-
// Add the script component
87-
component = tempObject.AddComponent(scriptType);
88-
}
89-
90-
// Apply field values if provided and component exists
91-
if (fieldValues != null && fieldValues.Count > 0 && component != null)
92-
{
93-
Undo.RecordObject(component, "Set field values");
94-
95-
foreach (var property in fieldValues.Properties())
96-
{
97-
try
98-
{
99-
// Get the field/property info
100-
var fieldInfo = component.GetType().GetField(property.Name,
101-
System.Reflection.BindingFlags.Public |
102-
System.Reflection.BindingFlags.NonPublic |
103-
System.Reflection.BindingFlags.Instance);
104-
105-
if (fieldInfo != null)
106-
{
107-
// Set field value
108-
object value = property.Value.ToObject(fieldInfo.FieldType);
109-
fieldInfo.SetValue(component, value);
110-
}
111-
else
112-
{
113-
// Try property
114-
var propInfo = component.GetType().GetProperty(property.Name,
115-
System.Reflection.BindingFlags.Public |
116-
System.Reflection.BindingFlags.NonPublic |
117-
System.Reflection.BindingFlags.Instance);
118-
119-
if (propInfo != null && propInfo.CanWrite)
120-
{
121-
object value = property.Value.ToObject(propInfo.PropertyType);
122-
propInfo.SetValue(component, value);
123-
}
124-
}
125-
}
126-
catch (Exception ex)
127-
{
128-
McpLogger.LogWarning($"Failed to set field '{property.Name}': {ex.Message}");
129-
}
130-
}
49+
// Add component
50+
Component component = AddComponent(tempObject, scriptName);
51+
52+
// Apply field values if provided and component exists
53+
ApplyFieldValues(fieldValues, component);
13154
}
132-
else if (fieldValues != null && fieldValues.Count > 0 && component == null)
55+
catch (Exception ex)
13356
{
134-
McpLogger.LogWarning("Field values provided but no script component to apply them to");
57+
return McpUnitySocketHandler.CreateErrorResponse(
58+
$"Failed to add component '{scriptName}' to GameObject",
59+
"component_error"
60+
);
13561
}
62+
}
63+
64+
// For safety, we'll create a unique name if prefab already exists
65+
int counter = 1;
66+
string prefabPath = $"{prefabName}.prefab";
67+
while (AssetDatabase.AssetPathToGUID(prefabPath) != "")
68+
{
69+
prefabPath = $"{prefabName}_{counter}.prefab";
70+
counter++;
71+
}
72+
73+
// Create the prefab
74+
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(tempObject, prefabPath);
75+
76+
// Clean up temporary object
77+
UnityEngine.Object.DestroyImmediate(tempObject);
78+
79+
// Refresh the asset database
80+
AssetDatabase.Refresh();
81+
82+
// Log the action
83+
McpLogger.LogInfo($"Created prefab '{prefab.name}' at path '{prefabPath}' from script '{scriptName}'");
84+
85+
// Create the response
86+
return new JObject
87+
{
88+
["success"] = true,
89+
["type"] = "text",
90+
["message"] = $"Successfully created prefab '{prefab.name}' at path '{prefabPath}'",
91+
["prefabPath"] = prefabPath
92+
};
93+
}
94+
95+
private Component AddComponent(GameObject gameObject, string scriptName)
96+
{
97+
// Find the script type
98+
Type scriptType = Type.GetType($"{scriptName}, Assembly-CSharp");
99+
if (scriptType == null)
100+
{
101+
// Try with just the class name
102+
scriptType = Type.GetType(scriptName);
103+
}
136104

137-
// Ensure Prefabs directory exists
138-
string prefabsPath = "Assets/Prefabs";
139-
if (!AssetDatabase.IsValidFolder(prefabsPath))
105+
if (scriptType == null)
106+
{
107+
// Try to find the type using AppDomain
108+
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
140109
{
141-
string guid = AssetDatabase.CreateFolder("Assets", "Prefabs");
142-
prefabsPath = AssetDatabase.GUIDToAssetPath(guid);
110+
scriptType = assembly.GetType(scriptName);
111+
if (scriptType != null)
112+
break;
143113
}
114+
}
144115

145-
// Create prefab path
146-
string prefabPath = $"{prefabsPath}/{prefabName}.prefab";
116+
// Throw an error if the type was not found
117+
if (scriptType == null)
118+
{
119+
return null;
120+
}
121+
122+
// Check if the type is a MonoBehaviour
123+
if (!typeof(MonoBehaviour).IsAssignableFrom(scriptType))
124+
{
125+
return null;
126+
}
127+
128+
return gameObject.AddComponent(scriptType);
129+
}
130+
131+
private void ApplyFieldValues(JObject fieldValues, Component component)
132+
{
133+
// Apply field values if provided and component exists
134+
if (fieldValues == null || fieldValues.Count == 0)
135+
{
136+
return;
137+
}
138+
139+
Undo.RecordObject(component, "Set field values");
147140

148-
// Check if prefab already exists
149-
if (AssetDatabase.AssetPathToGUID(prefabPath) != "")
141+
foreach (var property in fieldValues.Properties())
142+
{
143+
// Get the field/property info
144+
var fieldInfo = component.GetType().GetField(property.Name,
145+
System.Reflection.BindingFlags.Public |
146+
System.Reflection.BindingFlags.NonPublic |
147+
System.Reflection.BindingFlags.Instance);
148+
149+
if (fieldInfo != null)
150+
{
151+
// Set field value
152+
object value = property.Value.ToObject(fieldInfo.FieldType);
153+
fieldInfo.SetValue(component, value);
154+
}
155+
else
150156
{
151-
// For safety, we'll create a unique name
152-
int counter = 1;
153-
string basePrefabPath = prefabPath;
154-
while (AssetDatabase.AssetPathToGUID(prefabPath) != "")
157+
// Try property
158+
var propInfo = component.GetType().GetProperty(property.Name,
159+
System.Reflection.BindingFlags.Public |
160+
System.Reflection.BindingFlags.NonPublic |
161+
System.Reflection.BindingFlags.Instance);
162+
163+
if (propInfo != null && propInfo.CanWrite)
155164
{
156-
prefabPath = $"{prefabsPath}/{prefabName}_{counter}.prefab";
157-
counter++;
165+
object value = property.Value.ToObject(propInfo.PropertyType);
166+
propInfo.SetValue(component, value);
158167
}
159168
}
160-
161-
// Create the prefab
162-
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(tempObject, prefabPath);
163-
164-
// Clean up temporary object
165-
UnityEngine.Object.DestroyImmediate(tempObject);
166-
167-
// Refresh the asset database
168-
AssetDatabase.Refresh();
169-
170-
// Log the action
171-
McpLogger.LogInfo($"Created prefab '{prefab.name}' at path '{prefabPath}' from script '{scriptName}'");
172-
173-
// Create the response
174-
return new JObject
175-
{
176-
["success"] = true,
177-
["type"] = "text",
178-
["message"] = $"Successfully created prefab '{prefab.name}' at path '{prefabPath}'",
179-
["prefabPath"] = prefabPath
180-
};
181-
}
182-
catch (Exception ex)
183-
{
184-
return McpUnitySocketHandler.CreateErrorResponse(
185-
$"Error creating prefab: {ex.Message}",
186-
"creation_error"
187-
);
188169
}
189170
}
190171
}

Editor/Tools/CreatePrefabTool.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Server~/src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { registerAddAssetToSceneTool } from './tools/addAssetToSceneTool.js';
1414
import { registerUpdateGameObjectTool } from './tools/updateGameObjectTool.js';
1515
import { registerCreatePrefabTool } from './tools/createPrefabTool.js';
1616
import { registerGetMenuItemsResource } from './resources/getMenuItemResource.js';
17-
import { registerPrefabCreationPrompt } from './prompts/prefabCreationPrompt.js';
1817
import { registerGetConsoleLogsResource } from './resources/getConsoleLogsResource.js';
1918
import { registerGetHierarchyResource } from './resources/getScenesHierarchyResource.js';
2019
import { registerGetPackagesResource } from './resources/getPackagesResource.js';
@@ -70,7 +69,6 @@ registerGetAssetsResource(server, mcpUnity, resourceLogger);
7069

7170
// Register all prompts into the MCP server
7271
registerGameObjectHandlingPrompt(server);
73-
registerPrefabCreationPrompt(server);
7472

7573
// Server startup function
7674
async function startServer() {

0 commit comments

Comments
 (0)