mcpforunityserver 9.3.0__py3-none-any.whl → 9.3.0b20260128055651__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli/commands/editor.py +27 -1
- cli/commands/prefab.py +134 -12
- cli/commands/texture.py +538 -0
- cli/commands/tool.py +61 -0
- cli/commands/vfx.py +51 -15
- cli/main.py +33 -0
- cli/utils/connection.py +37 -0
- cli/utils/suggestions.py +34 -0
- main.py +125 -6
- {mcpforunityserver-9.3.0.dist-info → mcpforunityserver-9.3.0b20260128055651.dist-info}/METADATA +2 -2
- {mcpforunityserver-9.3.0.dist-info → mcpforunityserver-9.3.0b20260128055651.dist-info}/RECORD +22 -17
- services/resources/prefab.py +191 -0
- services/tools/manage_components.py +1 -1
- services/tools/manage_gameobject.py +43 -23
- services/tools/manage_material.py +2 -2
- services/tools/manage_prefabs.py +128 -31
- services/tools/manage_texture.py +667 -0
- services/tools/manage_vfx.py +15 -633
- {mcpforunityserver-9.3.0.dist-info → mcpforunityserver-9.3.0b20260128055651.dist-info}/WHEEL +0 -0
- {mcpforunityserver-9.3.0.dist-info → mcpforunityserver-9.3.0b20260128055651.dist-info}/entry_points.txt +0 -0
- {mcpforunityserver-9.3.0.dist-info → mcpforunityserver-9.3.0b20260128055651.dist-info}/licenses/LICENSE +0 -0
- {mcpforunityserver-9.3.0.dist-info → mcpforunityserver-9.3.0b20260128055651.dist-info}/top_level.txt +0 -0
cli/commands/editor.py
CHANGED
|
@@ -6,7 +6,8 @@ from typing import Optional, Any
|
|
|
6
6
|
|
|
7
7
|
from cli.utils.config import get_config
|
|
8
8
|
from cli.utils.output import format_output, print_error, print_success, print_info
|
|
9
|
-
from cli.utils.connection import run_command, UnityConnectionError
|
|
9
|
+
from cli.utils.connection import run_command, run_list_custom_tools, UnityConnectionError
|
|
10
|
+
from cli.utils.suggestions import suggest_matches, format_suggestions
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@click.group()
|
|
@@ -472,6 +473,9 @@ def custom_tool(tool_name: str, params: str):
|
|
|
472
473
|
params_dict = json.loads(params)
|
|
473
474
|
except json.JSONDecodeError as e:
|
|
474
475
|
print_error(f"Invalid JSON for params: {e}")
|
|
476
|
+
print_info("Example: --params '{\"key\":\"value\"}'")
|
|
477
|
+
print_info(
|
|
478
|
+
"Tip: wrap JSON in single quotes to avoid shell escaping issues.")
|
|
475
479
|
sys.exit(1)
|
|
476
480
|
|
|
477
481
|
try:
|
|
@@ -482,6 +486,28 @@ def custom_tool(tool_name: str, params: str):
|
|
|
482
486
|
click.echo(format_output(result, config.format))
|
|
483
487
|
if result.get("success"):
|
|
484
488
|
print_success(f"Executed custom tool: {tool_name}")
|
|
489
|
+
else:
|
|
490
|
+
message = (result.get("message")
|
|
491
|
+
or result.get("error") or "").lower()
|
|
492
|
+
if "not found" in message and "tool" in message:
|
|
493
|
+
try:
|
|
494
|
+
tools_result = run_list_custom_tools(config)
|
|
495
|
+
tools = tools_result.get("tools")
|
|
496
|
+
if tools is None:
|
|
497
|
+
data = tools_result.get("data", {})
|
|
498
|
+
tools = data.get("tools") if isinstance(
|
|
499
|
+
data, dict) else None
|
|
500
|
+
names = [
|
|
501
|
+
t.get("name") for t in tools if isinstance(t, dict) and t.get("name")
|
|
502
|
+
] if isinstance(tools, list) else []
|
|
503
|
+
matches = suggest_matches(tool_name, names)
|
|
504
|
+
suggestion = format_suggestions(matches)
|
|
505
|
+
if suggestion:
|
|
506
|
+
print_info(suggestion)
|
|
507
|
+
print_info(
|
|
508
|
+
f'Example: unity-mcp editor custom-tool "{matches[0]}"')
|
|
509
|
+
except UnityConnectionError:
|
|
510
|
+
pass
|
|
485
511
|
except UnityConnectionError as e:
|
|
486
512
|
print_error(str(e))
|
|
487
513
|
sys.exit(1)
|
cli/commands/prefab.py
CHANGED
|
@@ -11,18 +11,13 @@ from cli.utils.connection import run_command, UnityConnectionError
|
|
|
11
11
|
|
|
12
12
|
@click.group()
|
|
13
13
|
def prefab():
|
|
14
|
-
"""Prefab operations - open, save, create prefabs."""
|
|
14
|
+
"""Prefab operations - info, hierarchy, open, save, close, create prefabs."""
|
|
15
15
|
pass
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@prefab.command("open")
|
|
19
19
|
@click.argument("path")
|
|
20
|
-
|
|
21
|
-
"--mode", "-m",
|
|
22
|
-
default="InIsolation",
|
|
23
|
-
help="Prefab stage mode (InIsolation)."
|
|
24
|
-
)
|
|
25
|
-
def open_stage(path: str, mode: str):
|
|
20
|
+
def open_stage(path: str):
|
|
26
21
|
"""Open a prefab in the prefab stage for editing.
|
|
27
22
|
|
|
28
23
|
\b
|
|
@@ -34,7 +29,6 @@ def open_stage(path: str, mode: str):
|
|
|
34
29
|
params: dict[str, Any] = {
|
|
35
30
|
"action": "open_stage",
|
|
36
31
|
"prefabPath": path,
|
|
37
|
-
"mode": mode,
|
|
38
32
|
}
|
|
39
33
|
|
|
40
34
|
try:
|
|
@@ -80,18 +74,29 @@ def close_stage(save: bool):
|
|
|
80
74
|
|
|
81
75
|
|
|
82
76
|
@prefab.command("save")
|
|
83
|
-
|
|
77
|
+
@click.option(
|
|
78
|
+
"--force", "-f",
|
|
79
|
+
is_flag=True,
|
|
80
|
+
help="Force save even if no changes detected. Useful for automated workflows."
|
|
81
|
+
)
|
|
82
|
+
def save_stage(force: bool):
|
|
84
83
|
"""Save the currently open prefab stage.
|
|
85
84
|
|
|
86
85
|
\b
|
|
87
86
|
Examples:
|
|
88
87
|
unity-mcp prefab save
|
|
88
|
+
unity-mcp prefab save --force
|
|
89
89
|
"""
|
|
90
90
|
config = get_config()
|
|
91
91
|
|
|
92
|
+
params: dict[str, Any] = {
|
|
93
|
+
"action": "save_open_stage",
|
|
94
|
+
}
|
|
95
|
+
if force:
|
|
96
|
+
params["force"] = True
|
|
97
|
+
|
|
92
98
|
try:
|
|
93
|
-
result = run_command("manage_prefabs",
|
|
94
|
-
"action": "save_open_stage"}, config)
|
|
99
|
+
result = run_command("manage_prefabs", params, config)
|
|
95
100
|
click.echo(format_output(result, config.format))
|
|
96
101
|
if result.get("success"):
|
|
97
102
|
print_success("Saved prefab")
|
|
@@ -100,6 +105,115 @@ def save_stage():
|
|
|
100
105
|
sys.exit(1)
|
|
101
106
|
|
|
102
107
|
|
|
108
|
+
@prefab.command("info")
|
|
109
|
+
@click.argument("path")
|
|
110
|
+
@click.option(
|
|
111
|
+
"--compact", "-c",
|
|
112
|
+
is_flag=True,
|
|
113
|
+
help="Show compact output (key values only)."
|
|
114
|
+
)
|
|
115
|
+
def info(path: str, compact: bool):
|
|
116
|
+
"""Get information about a prefab asset.
|
|
117
|
+
|
|
118
|
+
\b
|
|
119
|
+
Examples:
|
|
120
|
+
unity-mcp prefab info "Assets/Prefabs/Player.prefab"
|
|
121
|
+
unity-mcp prefab info "Assets/Prefabs/UI.prefab" --compact
|
|
122
|
+
"""
|
|
123
|
+
config = get_config()
|
|
124
|
+
|
|
125
|
+
params: dict[str, Any] = {
|
|
126
|
+
"action": "get_info",
|
|
127
|
+
"prefabPath": path,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
result = run_command("manage_prefabs", params, config)
|
|
132
|
+
# Get the actual response data from the wrapped result structure
|
|
133
|
+
response_data = result.get("result", result)
|
|
134
|
+
if compact and response_data.get("success") and response_data.get("data"):
|
|
135
|
+
data = response_data["data"]
|
|
136
|
+
click.echo(f"Prefab: {data.get('assetPath', path)}")
|
|
137
|
+
click.echo(f" Type: {data.get('prefabType', 'Unknown')}")
|
|
138
|
+
click.echo(f" Root: {data.get('rootObjectName', 'N/A')}")
|
|
139
|
+
click.echo(f" GUID: {data.get('guid', 'N/A')}")
|
|
140
|
+
click.echo(
|
|
141
|
+
f" Components: {len(data.get('rootComponentTypes', []))}")
|
|
142
|
+
click.echo(f" Children: {data.get('childCount', 0)}")
|
|
143
|
+
if data.get('isVariant'):
|
|
144
|
+
click.echo(f" Variant of: {data.get('parentPrefab', 'N/A')}")
|
|
145
|
+
else:
|
|
146
|
+
click.echo(format_output(result, config.format))
|
|
147
|
+
except UnityConnectionError as e:
|
|
148
|
+
print_error(str(e))
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@prefab.command("hierarchy")
|
|
153
|
+
@click.argument("path")
|
|
154
|
+
@click.option(
|
|
155
|
+
"--compact", "-c",
|
|
156
|
+
is_flag=True,
|
|
157
|
+
help="Show compact output (names and paths only)."
|
|
158
|
+
)
|
|
159
|
+
@click.option(
|
|
160
|
+
"--show-prefab-info", "-p",
|
|
161
|
+
is_flag=True,
|
|
162
|
+
help="Show prefab nesting information."
|
|
163
|
+
)
|
|
164
|
+
def hierarchy(path: str, compact: bool, show_prefab_info: bool):
|
|
165
|
+
"""Get the hierarchical structure of a prefab.
|
|
166
|
+
|
|
167
|
+
\b
|
|
168
|
+
Examples:
|
|
169
|
+
unity-mcp prefab hierarchy "Assets/Prefabs/Player.prefab"
|
|
170
|
+
unity-mcp prefab hierarchy "Assets/Prefabs/UI.prefab" --compact
|
|
171
|
+
unity-mcp prefab hierarchy "Assets/Prefabs/Complex.prefab" --show-prefab-info
|
|
172
|
+
"""
|
|
173
|
+
config = get_config()
|
|
174
|
+
|
|
175
|
+
params: dict[str, Any] = {
|
|
176
|
+
"action": "get_hierarchy",
|
|
177
|
+
"prefabPath": path,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
result = run_command("manage_prefabs", params, config)
|
|
182
|
+
# Get the actual response data from the wrapped result structure
|
|
183
|
+
response_data = result.get("result", result)
|
|
184
|
+
if compact and response_data.get("success") and response_data.get("data"):
|
|
185
|
+
data = response_data["data"]
|
|
186
|
+
items = data.get("items", [])
|
|
187
|
+
for item in items:
|
|
188
|
+
indent = " " * item.get("path", "").count("/")
|
|
189
|
+
prefab_info = ""
|
|
190
|
+
if show_prefab_info and item.get("prefab", {}).get("isNestedRoot"):
|
|
191
|
+
prefab_info = f" [nested: {item['prefab']['assetPath']}]"
|
|
192
|
+
click.echo(f"{indent}{item.get('name')}{prefab_info}")
|
|
193
|
+
click.echo(f"\nTotal: {data.get('total', 0)} objects")
|
|
194
|
+
elif show_prefab_info:
|
|
195
|
+
# Show prefab info in readable format
|
|
196
|
+
if response_data.get("success") and response_data.get("data"):
|
|
197
|
+
data = response_data["data"]
|
|
198
|
+
items = data.get("items", [])
|
|
199
|
+
for item in items:
|
|
200
|
+
prefab = item.get("prefab", {})
|
|
201
|
+
prefab_info = ""
|
|
202
|
+
if prefab.get("isRoot"):
|
|
203
|
+
prefab_info = " [root]"
|
|
204
|
+
elif prefab.get("isNestedRoot"):
|
|
205
|
+
prefab_info = f" [nested: {prefab.get('nestingDepth', 0)}]"
|
|
206
|
+
click.echo(f"{item.get('path')}{prefab_info}")
|
|
207
|
+
click.echo(f"\nTotal: {data.get('total', 0)} objects")
|
|
208
|
+
else:
|
|
209
|
+
click.echo(format_output(result, config.format))
|
|
210
|
+
else:
|
|
211
|
+
click.echo(format_output(result, config.format))
|
|
212
|
+
except UnityConnectionError as e:
|
|
213
|
+
print_error(str(e))
|
|
214
|
+
sys.exit(1)
|
|
215
|
+
|
|
216
|
+
|
|
103
217
|
@prefab.command("create")
|
|
104
218
|
@click.argument("target")
|
|
105
219
|
@click.argument("path")
|
|
@@ -113,13 +227,19 @@ def save_stage():
|
|
|
113
227
|
is_flag=True,
|
|
114
228
|
help="Include inactive objects when finding target."
|
|
115
229
|
)
|
|
116
|
-
|
|
230
|
+
@click.option(
|
|
231
|
+
"--unlink-if-instance",
|
|
232
|
+
is_flag=True,
|
|
233
|
+
help="Unlink from existing prefab before creating new one."
|
|
234
|
+
)
|
|
235
|
+
def create(target: str, path: str, overwrite: bool, include_inactive: bool, unlink_if_instance: bool):
|
|
117
236
|
"""Create a prefab from a scene GameObject.
|
|
118
237
|
|
|
119
238
|
\b
|
|
120
239
|
Examples:
|
|
121
240
|
unity-mcp prefab create "Player" "Assets/Prefabs/Player.prefab"
|
|
122
241
|
unity-mcp prefab create "Enemy" "Assets/Prefabs/Enemy.prefab" --overwrite
|
|
242
|
+
unity-mcp prefab create "EnemyInstance" "Assets/Prefabs/BossEnemy.prefab" --unlink-if-instance
|
|
123
243
|
"""
|
|
124
244
|
config = get_config()
|
|
125
245
|
|
|
@@ -133,6 +253,8 @@ def create(target: str, path: str, overwrite: bool, include_inactive: bool):
|
|
|
133
253
|
params["allowOverwrite"] = True
|
|
134
254
|
if include_inactive:
|
|
135
255
|
params["searchInactive"] = True
|
|
256
|
+
if unlink_if_instance:
|
|
257
|
+
params["unlinkIfInstance"] = True
|
|
136
258
|
|
|
137
259
|
try:
|
|
138
260
|
result = run_command("manage_prefabs", params, config)
|