mcpforunityserver 9.4.0b20260203025228__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/__init__.py +3 -0
- cli/commands/__init__.py +3 -0
- cli/commands/animation.py +84 -0
- cli/commands/asset.py +280 -0
- cli/commands/audio.py +125 -0
- cli/commands/batch.py +171 -0
- cli/commands/code.py +182 -0
- cli/commands/component.py +190 -0
- cli/commands/editor.py +447 -0
- cli/commands/gameobject.py +487 -0
- cli/commands/instance.py +93 -0
- cli/commands/lighting.py +123 -0
- cli/commands/material.py +239 -0
- cli/commands/prefab.py +248 -0
- cli/commands/scene.py +231 -0
- cli/commands/script.py +222 -0
- cli/commands/shader.py +226 -0
- cli/commands/texture.py +540 -0
- cli/commands/tool.py +58 -0
- cli/commands/ui.py +258 -0
- cli/commands/vfx.py +421 -0
- cli/main.py +281 -0
- cli/utils/__init__.py +31 -0
- cli/utils/config.py +58 -0
- cli/utils/confirmation.py +37 -0
- cli/utils/connection.py +254 -0
- cli/utils/constants.py +23 -0
- cli/utils/output.py +195 -0
- cli/utils/parsers.py +112 -0
- cli/utils/suggestions.py +34 -0
- core/__init__.py +0 -0
- core/config.py +67 -0
- core/constants.py +4 -0
- core/logging_decorator.py +37 -0
- core/telemetry.py +551 -0
- core/telemetry_decorator.py +164 -0
- main.py +845 -0
- mcpforunityserver-9.4.0b20260203025228.dist-info/METADATA +328 -0
- mcpforunityserver-9.4.0b20260203025228.dist-info/RECORD +105 -0
- mcpforunityserver-9.4.0b20260203025228.dist-info/WHEEL +5 -0
- mcpforunityserver-9.4.0b20260203025228.dist-info/entry_points.txt +3 -0
- mcpforunityserver-9.4.0b20260203025228.dist-info/licenses/LICENSE +21 -0
- mcpforunityserver-9.4.0b20260203025228.dist-info/top_level.txt +7 -0
- models/__init__.py +4 -0
- models/models.py +56 -0
- models/unity_response.py +70 -0
- services/__init__.py +0 -0
- services/api_key_service.py +235 -0
- services/custom_tool_service.py +499 -0
- services/registry/__init__.py +22 -0
- services/registry/resource_registry.py +53 -0
- services/registry/tool_registry.py +51 -0
- services/resources/__init__.py +86 -0
- services/resources/active_tool.py +48 -0
- services/resources/custom_tools.py +57 -0
- services/resources/editor_state.py +304 -0
- services/resources/gameobject.py +243 -0
- services/resources/layers.py +30 -0
- services/resources/menu_items.py +35 -0
- services/resources/prefab.py +191 -0
- services/resources/prefab_stage.py +40 -0
- services/resources/project_info.py +40 -0
- services/resources/selection.py +56 -0
- services/resources/tags.py +31 -0
- services/resources/tests.py +88 -0
- services/resources/unity_instances.py +125 -0
- services/resources/windows.py +48 -0
- services/state/external_changes_scanner.py +245 -0
- services/tools/__init__.py +83 -0
- services/tools/batch_execute.py +93 -0
- services/tools/debug_request_context.py +86 -0
- services/tools/execute_custom_tool.py +43 -0
- services/tools/execute_menu_item.py +32 -0
- services/tools/find_gameobjects.py +110 -0
- services/tools/find_in_file.py +181 -0
- services/tools/manage_asset.py +119 -0
- services/tools/manage_components.py +131 -0
- services/tools/manage_editor.py +64 -0
- services/tools/manage_gameobject.py +260 -0
- services/tools/manage_material.py +111 -0
- services/tools/manage_prefabs.py +209 -0
- services/tools/manage_scene.py +111 -0
- services/tools/manage_script.py +645 -0
- services/tools/manage_scriptable_object.py +87 -0
- services/tools/manage_shader.py +71 -0
- services/tools/manage_texture.py +581 -0
- services/tools/manage_vfx.py +120 -0
- services/tools/preflight.py +110 -0
- services/tools/read_console.py +151 -0
- services/tools/refresh_unity.py +153 -0
- services/tools/run_tests.py +317 -0
- services/tools/script_apply_edits.py +1006 -0
- services/tools/set_active_instance.py +120 -0
- services/tools/utils.py +348 -0
- transport/__init__.py +0 -0
- transport/legacy/port_discovery.py +329 -0
- transport/legacy/stdio_port_registry.py +65 -0
- transport/legacy/unity_connection.py +910 -0
- transport/models.py +68 -0
- transport/plugin_hub.py +787 -0
- transport/plugin_registry.py +182 -0
- transport/unity_instance_middleware.py +262 -0
- transport/unity_transport.py +94 -0
- utils/focus_nudge.py +589 -0
- utils/module_discovery.py +55 -0
cli/__init__.py
ADDED
cli/commands/__init__.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Animation CLI commands - placeholder for future implementation."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from typing import Optional, Any
|
|
5
|
+
|
|
6
|
+
from cli.utils.config import get_config
|
|
7
|
+
from cli.utils.output import format_output, print_error, print_info
|
|
8
|
+
from cli.utils.connection import run_command, handle_unity_errors
|
|
9
|
+
from cli.utils.constants import SEARCH_METHOD_CHOICE_BASIC
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def animation():
|
|
14
|
+
"""Animation operations - control Animator, play animations."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@animation.command("play")
|
|
19
|
+
@click.argument("target")
|
|
20
|
+
@click.argument("state_name")
|
|
21
|
+
@click.option(
|
|
22
|
+
"--layer", "-l",
|
|
23
|
+
default=0,
|
|
24
|
+
type=int,
|
|
25
|
+
help="Animator layer(TODO)."
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
"--search-method",
|
|
29
|
+
type=SEARCH_METHOD_CHOICE_BASIC,
|
|
30
|
+
default=None,
|
|
31
|
+
help="How to find the target."
|
|
32
|
+
)
|
|
33
|
+
@handle_unity_errors
|
|
34
|
+
def play(target: str, state_name: str, layer: int, search_method: Optional[str]):
|
|
35
|
+
"""Play an animation state on a target's Animator.
|
|
36
|
+
|
|
37
|
+
\b
|
|
38
|
+
Examples:
|
|
39
|
+
unity-mcp animation play "Player" "Walk"
|
|
40
|
+
unity-mcp animation play "Enemy" "Attack" --layer 1
|
|
41
|
+
"""
|
|
42
|
+
config = get_config()
|
|
43
|
+
|
|
44
|
+
# Set Animator parameter to trigger state
|
|
45
|
+
params: dict[str, Any] = {
|
|
46
|
+
"action": "set_property",
|
|
47
|
+
"target": target,
|
|
48
|
+
"componentType": "Animator",
|
|
49
|
+
"property": "Play",
|
|
50
|
+
"value": state_name,
|
|
51
|
+
"layer": layer,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if search_method:
|
|
55
|
+
params["searchMethod"] = search_method
|
|
56
|
+
|
|
57
|
+
result = run_command("manage_components", params, config)
|
|
58
|
+
click.echo(format_output(result, config.format))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@animation.command("set-parameter")
|
|
62
|
+
@click.argument("target")
|
|
63
|
+
@click.argument("param_name")
|
|
64
|
+
@click.argument("value")
|
|
65
|
+
@click.option(
|
|
66
|
+
"--type", "-t",
|
|
67
|
+
"param_type",
|
|
68
|
+
type=click.Choice(["float", "int", "bool", "trigger"]),
|
|
69
|
+
default="float",
|
|
70
|
+
help="Parameter type."
|
|
71
|
+
)
|
|
72
|
+
def set_parameter(target: str, param_name: str, value: str, param_type: str):
|
|
73
|
+
"""Set an Animator parameter.
|
|
74
|
+
|
|
75
|
+
\b
|
|
76
|
+
Examples:
|
|
77
|
+
unity-mcp animation set-parameter "Player" "Speed" 5.0
|
|
78
|
+
unity-mcp animation set-parameter "Player" "IsRunning" true --type bool
|
|
79
|
+
unity-mcp animation set-parameter "Player" "Jump" "" --type trigger
|
|
80
|
+
"""
|
|
81
|
+
config = get_config()
|
|
82
|
+
print_info(
|
|
83
|
+
"Animation parameter command - requires custom Unity implementation")
|
|
84
|
+
click.echo(f"Would set {param_name}={value} ({param_type}) on {target}")
|
cli/commands/asset.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Asset CLI commands."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import json
|
|
5
|
+
import click
|
|
6
|
+
from typing import Optional, Any
|
|
7
|
+
|
|
8
|
+
from cli.utils.config import get_config
|
|
9
|
+
from cli.utils.output import format_output, print_error, print_success
|
|
10
|
+
from cli.utils.connection import run_command, handle_unity_errors
|
|
11
|
+
from cli.utils.parsers import parse_json_dict_or_exit
|
|
12
|
+
from cli.utils.confirmation import confirm_destructive_action
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group()
|
|
16
|
+
def asset():
|
|
17
|
+
"""Asset operations - search, import, create, delete assets."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@asset.command("search")
|
|
22
|
+
@click.argument("pattern", default="*")
|
|
23
|
+
@click.option(
|
|
24
|
+
"--path", "-p",
|
|
25
|
+
default="Assets",
|
|
26
|
+
help="Folder path to search in."
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--type", "-t",
|
|
30
|
+
"filter_type",
|
|
31
|
+
default=None,
|
|
32
|
+
help="Filter by asset type (e.g., Material, Prefab, MonoScript)."
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--limit", "-l",
|
|
36
|
+
default=25,
|
|
37
|
+
type=int,
|
|
38
|
+
help="Maximum results per page."
|
|
39
|
+
)
|
|
40
|
+
@click.option(
|
|
41
|
+
"--page",
|
|
42
|
+
default=1,
|
|
43
|
+
type=int,
|
|
44
|
+
help="Page number (1-based)."
|
|
45
|
+
)
|
|
46
|
+
@handle_unity_errors
|
|
47
|
+
def search(pattern: str, path: str, filter_type: Optional[str], limit: int, page: int):
|
|
48
|
+
"""Search for assets.
|
|
49
|
+
|
|
50
|
+
\b
|
|
51
|
+
Examples:
|
|
52
|
+
unity-mcp asset search "*.prefab"
|
|
53
|
+
unity-mcp asset search "Player*" --path "Assets/Characters"
|
|
54
|
+
unity-mcp asset search "*" --type Material
|
|
55
|
+
unity-mcp asset search "t:MonoScript" --path "Assets/Scripts"
|
|
56
|
+
"""
|
|
57
|
+
config = get_config()
|
|
58
|
+
|
|
59
|
+
params: dict[str, Any] = {
|
|
60
|
+
"action": "search",
|
|
61
|
+
"path": path,
|
|
62
|
+
"searchPattern": pattern,
|
|
63
|
+
"pageSize": limit,
|
|
64
|
+
"pageNumber": page,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if filter_type:
|
|
68
|
+
params["filterType"] = filter_type
|
|
69
|
+
|
|
70
|
+
result = run_command("manage_asset", params, config)
|
|
71
|
+
click.echo(format_output(result, config.format))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@asset.command("info")
|
|
75
|
+
@click.argument("path")
|
|
76
|
+
@click.option(
|
|
77
|
+
"--preview",
|
|
78
|
+
is_flag=True,
|
|
79
|
+
help="Generate preview thumbnail (may be large)."
|
|
80
|
+
)
|
|
81
|
+
@handle_unity_errors
|
|
82
|
+
def info(path: str, preview: bool):
|
|
83
|
+
"""Get detailed information about an asset.
|
|
84
|
+
|
|
85
|
+
\b
|
|
86
|
+
Examples:
|
|
87
|
+
unity-mcp asset info "Assets/Materials/Red.mat"
|
|
88
|
+
unity-mcp asset info "Assets/Prefabs/Player.prefab" --preview
|
|
89
|
+
"""
|
|
90
|
+
config = get_config()
|
|
91
|
+
|
|
92
|
+
params: dict[str, Any] = {
|
|
93
|
+
"action": "get_info",
|
|
94
|
+
"path": path,
|
|
95
|
+
"generatePreview": preview,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
result = run_command("manage_asset", params, config)
|
|
99
|
+
click.echo(format_output(result, config.format))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@asset.command("create")
|
|
103
|
+
@click.argument("path")
|
|
104
|
+
@click.argument("asset_type")
|
|
105
|
+
@click.option(
|
|
106
|
+
"--properties", "-p",
|
|
107
|
+
default=None,
|
|
108
|
+
help='Initial properties as JSON.'
|
|
109
|
+
)
|
|
110
|
+
@handle_unity_errors
|
|
111
|
+
def create(path: str, asset_type: str, properties: Optional[str]):
|
|
112
|
+
"""Create a new asset.
|
|
113
|
+
|
|
114
|
+
\b
|
|
115
|
+
Examples:
|
|
116
|
+
unity-mcp asset create "Assets/Materials/Blue.mat" Material
|
|
117
|
+
unity-mcp asset create "Assets/NewFolder" Folder
|
|
118
|
+
unity-mcp asset create "Assets/Materials/Custom.mat" Material --properties '{"color": [0,0,1,1]}'
|
|
119
|
+
"""
|
|
120
|
+
config = get_config()
|
|
121
|
+
|
|
122
|
+
params: dict[str, Any] = {
|
|
123
|
+
"action": "create",
|
|
124
|
+
"path": path,
|
|
125
|
+
"assetType": asset_type,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if properties:
|
|
129
|
+
params["properties"] = parse_json_dict_or_exit(properties, "properties")
|
|
130
|
+
|
|
131
|
+
result = run_command("manage_asset", params, config)
|
|
132
|
+
click.echo(format_output(result, config.format))
|
|
133
|
+
if result.get("success"):
|
|
134
|
+
print_success(f"Created {asset_type}: {path}")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@asset.command("delete")
|
|
138
|
+
@click.argument("path")
|
|
139
|
+
@click.option(
|
|
140
|
+
"--force", "-f",
|
|
141
|
+
is_flag=True,
|
|
142
|
+
help="Skip confirmation prompt."
|
|
143
|
+
)
|
|
144
|
+
@handle_unity_errors
|
|
145
|
+
def delete(path: str, force: bool):
|
|
146
|
+
"""Delete an asset.
|
|
147
|
+
|
|
148
|
+
\b
|
|
149
|
+
Examples:
|
|
150
|
+
unity-mcp asset delete "Assets/OldMaterial.mat"
|
|
151
|
+
unity-mcp asset delete "Assets/Unused" --force
|
|
152
|
+
"""
|
|
153
|
+
config = get_config()
|
|
154
|
+
|
|
155
|
+
confirm_destructive_action("Delete", "asset", path, force)
|
|
156
|
+
|
|
157
|
+
result = run_command(
|
|
158
|
+
"manage_asset", {"action": "delete", "path": path}, config)
|
|
159
|
+
click.echo(format_output(result, config.format))
|
|
160
|
+
if result.get("success"):
|
|
161
|
+
print_success(f"Deleted: {path}")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@asset.command("duplicate")
|
|
165
|
+
@click.argument("source")
|
|
166
|
+
@click.argument("destination")
|
|
167
|
+
@handle_unity_errors
|
|
168
|
+
def duplicate(source: str, destination: str):
|
|
169
|
+
"""Duplicate an asset.
|
|
170
|
+
|
|
171
|
+
\b
|
|
172
|
+
Examples:
|
|
173
|
+
unity-mcp asset duplicate "Assets/Materials/Red.mat" "Assets/Materials/RedCopy.mat"
|
|
174
|
+
"""
|
|
175
|
+
config = get_config()
|
|
176
|
+
|
|
177
|
+
params: dict[str, Any] = {
|
|
178
|
+
"action": "duplicate",
|
|
179
|
+
"path": source,
|
|
180
|
+
"destination": destination,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
result = run_command("manage_asset", params, config)
|
|
184
|
+
click.echo(format_output(result, config.format))
|
|
185
|
+
if result.get("success"):
|
|
186
|
+
print_success(f"Duplicated to: {destination}")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@asset.command("move")
|
|
190
|
+
@click.argument("source")
|
|
191
|
+
@click.argument("destination")
|
|
192
|
+
@handle_unity_errors
|
|
193
|
+
def move(source: str, destination: str):
|
|
194
|
+
"""Move an asset to a new location.
|
|
195
|
+
|
|
196
|
+
\b
|
|
197
|
+
Examples:
|
|
198
|
+
unity-mcp asset move "Assets/Old/Material.mat" "Assets/New/Material.mat"
|
|
199
|
+
"""
|
|
200
|
+
config = get_config()
|
|
201
|
+
|
|
202
|
+
params: dict[str, Any] = {
|
|
203
|
+
"action": "move",
|
|
204
|
+
"path": source,
|
|
205
|
+
"destination": destination,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
result = run_command("manage_asset", params, config)
|
|
209
|
+
click.echo(format_output(result, config.format))
|
|
210
|
+
if result.get("success"):
|
|
211
|
+
print_success(f"Moved to: {destination}")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@asset.command("rename")
|
|
215
|
+
@click.argument("path")
|
|
216
|
+
@click.argument("new_name")
|
|
217
|
+
@handle_unity_errors
|
|
218
|
+
def rename(path: str, new_name: str):
|
|
219
|
+
"""Rename an asset.
|
|
220
|
+
|
|
221
|
+
\b
|
|
222
|
+
Examples:
|
|
223
|
+
unity-mcp asset rename "Assets/Materials/Old.mat" "New.mat"
|
|
224
|
+
"""
|
|
225
|
+
config = get_config()
|
|
226
|
+
|
|
227
|
+
# Construct destination path
|
|
228
|
+
import os
|
|
229
|
+
dir_path = os.path.dirname(path)
|
|
230
|
+
destination = os.path.join(dir_path, new_name).replace("\\", "/")
|
|
231
|
+
|
|
232
|
+
params: dict[str, Any] = {
|
|
233
|
+
"action": "rename",
|
|
234
|
+
"path": path,
|
|
235
|
+
"destination": destination,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
result = run_command("manage_asset", params, config)
|
|
239
|
+
click.echo(format_output(result, config.format))
|
|
240
|
+
if result.get("success"):
|
|
241
|
+
print_success(f"Renamed to: {new_name}")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@asset.command("import")
|
|
245
|
+
@click.argument("path")
|
|
246
|
+
@handle_unity_errors
|
|
247
|
+
def import_asset(path: str):
|
|
248
|
+
"""Import/reimport an asset.
|
|
249
|
+
|
|
250
|
+
\b
|
|
251
|
+
Examples:
|
|
252
|
+
unity-mcp asset import "Assets/Textures/NewTexture.png"
|
|
253
|
+
"""
|
|
254
|
+
config = get_config()
|
|
255
|
+
|
|
256
|
+
result = run_command(
|
|
257
|
+
"manage_asset", {"action": "import", "path": path}, config)
|
|
258
|
+
click.echo(format_output(result, config.format))
|
|
259
|
+
if result.get("success"):
|
|
260
|
+
print_success(f"Imported: {path}")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@asset.command("mkdir")
|
|
264
|
+
@click.argument("path")
|
|
265
|
+
@handle_unity_errors
|
|
266
|
+
def mkdir(path: str):
|
|
267
|
+
"""Create a folder.
|
|
268
|
+
|
|
269
|
+
\b
|
|
270
|
+
Examples:
|
|
271
|
+
unity-mcp asset mkdir "Assets/NewFolder"
|
|
272
|
+
unity-mcp asset mkdir "Assets/Levels/Chapter1"
|
|
273
|
+
"""
|
|
274
|
+
config = get_config()
|
|
275
|
+
|
|
276
|
+
result = run_command(
|
|
277
|
+
"manage_asset", {"action": "create_folder", "path": path}, config)
|
|
278
|
+
click.echo(format_output(result, config.format))
|
|
279
|
+
if result.get("success"):
|
|
280
|
+
print_success(f"Created folder: {path}")
|
cli/commands/audio.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Audio CLI commands - placeholder for future implementation."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import click
|
|
5
|
+
from typing import Optional, Any
|
|
6
|
+
|
|
7
|
+
from cli.utils.config import get_config
|
|
8
|
+
from cli.utils.output import format_output, print_error, print_info
|
|
9
|
+
from cli.utils.connection import run_command, handle_unity_errors
|
|
10
|
+
from cli.utils.constants import SEARCH_METHOD_CHOICE_BASIC
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group()
|
|
14
|
+
def audio():
|
|
15
|
+
"""Audio operations - AudioSource control, audio settings."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@audio.command("play")
|
|
20
|
+
@click.argument("target")
|
|
21
|
+
@click.option(
|
|
22
|
+
"--clip", "-c",
|
|
23
|
+
default=None,
|
|
24
|
+
help="Audio clip path to play."
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--search-method",
|
|
28
|
+
type=SEARCH_METHOD_CHOICE_BASIC,
|
|
29
|
+
default=None,
|
|
30
|
+
help="How to find the target."
|
|
31
|
+
)
|
|
32
|
+
@handle_unity_errors
|
|
33
|
+
def play(target: str, clip: Optional[str], search_method: Optional[str]):
|
|
34
|
+
"""Play audio on a target's AudioSource.
|
|
35
|
+
|
|
36
|
+
\b
|
|
37
|
+
Examples:
|
|
38
|
+
unity-mcp audio play "MusicPlayer"
|
|
39
|
+
unity-mcp audio play "SFXSource" --clip "Assets/Audio/explosion.wav"
|
|
40
|
+
"""
|
|
41
|
+
config = get_config()
|
|
42
|
+
|
|
43
|
+
params: dict[str, Any] = {
|
|
44
|
+
"action": "set_property",
|
|
45
|
+
"target": target,
|
|
46
|
+
"componentType": "AudioSource",
|
|
47
|
+
"property": "Play",
|
|
48
|
+
"value": True,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if clip:
|
|
52
|
+
params["clip"] = clip
|
|
53
|
+
|
|
54
|
+
if search_method:
|
|
55
|
+
params["searchMethod"] = search_method
|
|
56
|
+
|
|
57
|
+
result = run_command("manage_components", params, config)
|
|
58
|
+
click.echo(format_output(result, config.format))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@audio.command("stop")
|
|
62
|
+
@click.argument("target")
|
|
63
|
+
@click.option(
|
|
64
|
+
"--search-method",
|
|
65
|
+
type=SEARCH_METHOD_CHOICE_BASIC,
|
|
66
|
+
default=None,
|
|
67
|
+
help="How to find the target."
|
|
68
|
+
)
|
|
69
|
+
@handle_unity_errors
|
|
70
|
+
def stop(target: str, search_method: Optional[str]):
|
|
71
|
+
"""Stop audio on a target's AudioSource.
|
|
72
|
+
|
|
73
|
+
\b
|
|
74
|
+
Examples:
|
|
75
|
+
unity-mcp audio stop "MusicPlayer"
|
|
76
|
+
"""
|
|
77
|
+
config = get_config()
|
|
78
|
+
|
|
79
|
+
params: dict[str, Any] = {
|
|
80
|
+
"action": "set_property",
|
|
81
|
+
"target": target,
|
|
82
|
+
"componentType": "AudioSource",
|
|
83
|
+
"property": "Stop",
|
|
84
|
+
"value": True,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if search_method:
|
|
88
|
+
params["searchMethod"] = search_method
|
|
89
|
+
|
|
90
|
+
result = run_command("manage_components", params, config)
|
|
91
|
+
click.echo(format_output(result, config.format))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@audio.command("volume")
|
|
95
|
+
@click.argument("target")
|
|
96
|
+
@click.argument("level", type=float)
|
|
97
|
+
@click.option(
|
|
98
|
+
"--search-method",
|
|
99
|
+
type=SEARCH_METHOD_CHOICE_BASIC,
|
|
100
|
+
default=None,
|
|
101
|
+
help="How to find the target."
|
|
102
|
+
)
|
|
103
|
+
@handle_unity_errors
|
|
104
|
+
def volume(target: str, level: float, search_method: Optional[str]):
|
|
105
|
+
"""Set audio volume on a target's AudioSource.
|
|
106
|
+
|
|
107
|
+
\b
|
|
108
|
+
Examples:
|
|
109
|
+
unity-mcp audio volume "MusicPlayer" 0.5
|
|
110
|
+
"""
|
|
111
|
+
config = get_config()
|
|
112
|
+
|
|
113
|
+
params: dict[str, Any] = {
|
|
114
|
+
"action": "set_property",
|
|
115
|
+
"target": target,
|
|
116
|
+
"componentType": "AudioSource",
|
|
117
|
+
"property": "volume",
|
|
118
|
+
"value": level,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if search_method:
|
|
122
|
+
params["searchMethod"] = search_method
|
|
123
|
+
|
|
124
|
+
result = run_command("manage_components", params, config)
|
|
125
|
+
click.echo(format_output(result, config.format))
|
cli/commands/batch.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Batch CLI commands for executing multiple Unity operations efficiently."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import json
|
|
5
|
+
import click
|
|
6
|
+
from typing import Optional, Any
|
|
7
|
+
|
|
8
|
+
from cli.utils.config import get_config
|
|
9
|
+
from cli.utils.output import format_output, print_error, print_success, print_info
|
|
10
|
+
from cli.utils.connection import run_command, handle_unity_errors
|
|
11
|
+
from cli.utils.parsers import parse_json_list_or_exit
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group()
|
|
15
|
+
def batch():
|
|
16
|
+
"""Batch operations - execute multiple commands efficiently."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@batch.command("run")
|
|
21
|
+
@click.argument("file", type=click.Path(exists=True))
|
|
22
|
+
@click.option("--parallel", is_flag=True, help="Execute read-only commands in parallel.")
|
|
23
|
+
@click.option("--fail-fast", is_flag=True, help="Stop on first failure.")
|
|
24
|
+
@handle_unity_errors
|
|
25
|
+
def batch_run(file: str, parallel: bool, fail_fast: bool):
|
|
26
|
+
"""Execute commands from a JSON file.
|
|
27
|
+
|
|
28
|
+
The JSON file should contain an array of command objects with 'tool' and 'params' keys.
|
|
29
|
+
|
|
30
|
+
\\b
|
|
31
|
+
File format:
|
|
32
|
+
[
|
|
33
|
+
{"tool": "manage_gameobject", "params": {"action": "create", "name": "Cube1"}},
|
|
34
|
+
{"tool": "manage_gameobject", "params": {"action": "create", "name": "Cube2"}},
|
|
35
|
+
{"tool": "manage_components", "params": {"action": "add", "target": "Cube1", "componentType": "Rigidbody"}}
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
\\b
|
|
39
|
+
Examples:
|
|
40
|
+
unity-mcp batch run commands.json
|
|
41
|
+
unity-mcp batch run setup.json --parallel
|
|
42
|
+
unity-mcp batch run critical.json --fail-fast
|
|
43
|
+
"""
|
|
44
|
+
config = get_config()
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
with open(file, 'r') as f:
|
|
48
|
+
commands = json.load(f)
|
|
49
|
+
except json.JSONDecodeError as e:
|
|
50
|
+
print_error(f"Invalid JSON in file: {e}")
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
except IOError as e:
|
|
53
|
+
print_error(f"Error reading file: {e}")
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
|
|
56
|
+
if not isinstance(commands, list):
|
|
57
|
+
print_error("JSON file must contain an array of commands")
|
|
58
|
+
sys.exit(1)
|
|
59
|
+
|
|
60
|
+
if len(commands) > 25:
|
|
61
|
+
print_error(f"Maximum 25 commands per batch, got {len(commands)}")
|
|
62
|
+
sys.exit(1)
|
|
63
|
+
|
|
64
|
+
params: dict[str, Any] = {"commands": commands}
|
|
65
|
+
if parallel:
|
|
66
|
+
params["parallel"] = True
|
|
67
|
+
if fail_fast:
|
|
68
|
+
params["failFast"] = True
|
|
69
|
+
|
|
70
|
+
click.echo(f"Executing {len(commands)} commands...")
|
|
71
|
+
|
|
72
|
+
result = run_command("batch_execute", params, config)
|
|
73
|
+
click.echo(format_output(result, config.format))
|
|
74
|
+
|
|
75
|
+
if isinstance(result, dict):
|
|
76
|
+
results = result.get("data", {}).get("results", [])
|
|
77
|
+
succeeded = sum(1 for r in results if r.get("success"))
|
|
78
|
+
failed = len(results) - succeeded
|
|
79
|
+
|
|
80
|
+
if failed == 0:
|
|
81
|
+
print_success(
|
|
82
|
+
f"All {succeeded} commands completed successfully")
|
|
83
|
+
else:
|
|
84
|
+
print_info(f"{succeeded} succeeded, {failed} failed")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@batch.command("inline")
|
|
88
|
+
@click.argument("commands_json")
|
|
89
|
+
@click.option("--parallel", is_flag=True, help="Execute read-only commands in parallel.")
|
|
90
|
+
@click.option("--fail-fast", is_flag=True, help="Stop on first failure.")
|
|
91
|
+
@handle_unity_errors
|
|
92
|
+
def batch_inline(commands_json: str, parallel: bool, fail_fast: bool):
|
|
93
|
+
"""Execute commands from inline JSON.
|
|
94
|
+
|
|
95
|
+
\\b
|
|
96
|
+
Examples:
|
|
97
|
+
unity-mcp batch inline '[{"tool": "manage_scene", "params": {"action": "get_active"}}]'
|
|
98
|
+
|
|
99
|
+
unity-mcp batch inline '[
|
|
100
|
+
{"tool": "manage_gameobject", "params": {"action": "create", "name": "A", "primitiveType": "Cube"}},
|
|
101
|
+
{"tool": "manage_gameobject", "params": {"action": "create", "name": "B", "primitiveType": "Sphere"}}
|
|
102
|
+
]'
|
|
103
|
+
"""
|
|
104
|
+
config = get_config()
|
|
105
|
+
|
|
106
|
+
commands = parse_json_list_or_exit(commands_json, "commands")
|
|
107
|
+
|
|
108
|
+
if len(commands) > 25:
|
|
109
|
+
print_error(f"Maximum 25 commands per batch, got {len(commands)}")
|
|
110
|
+
sys.exit(1)
|
|
111
|
+
|
|
112
|
+
params: dict[str, Any] = {"commands": commands}
|
|
113
|
+
if parallel:
|
|
114
|
+
params["parallel"] = True
|
|
115
|
+
if fail_fast:
|
|
116
|
+
params["failFast"] = True
|
|
117
|
+
|
|
118
|
+
result = run_command("batch_execute", params, config)
|
|
119
|
+
click.echo(format_output(result, config.format))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@batch.command("template")
|
|
123
|
+
@click.option("--output", "-o", type=click.Path(), help="Output file (default: stdout)")
|
|
124
|
+
def batch_template(output: Optional[str]):
|
|
125
|
+
"""Generate a sample batch commands file.
|
|
126
|
+
|
|
127
|
+
\\b
|
|
128
|
+
Examples:
|
|
129
|
+
unity-mcp batch template > commands.json
|
|
130
|
+
unity-mcp batch template -o my_batch.json
|
|
131
|
+
"""
|
|
132
|
+
template = [
|
|
133
|
+
{
|
|
134
|
+
"tool": "manage_scene",
|
|
135
|
+
"params": {"action": "get_active"}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"tool": "manage_gameobject",
|
|
139
|
+
"params": {
|
|
140
|
+
"action": "create",
|
|
141
|
+
"name": "BatchCube",
|
|
142
|
+
"primitiveType": "Cube",
|
|
143
|
+
"position": [0, 1, 0]
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"tool": "manage_components",
|
|
148
|
+
"params": {
|
|
149
|
+
"action": "add",
|
|
150
|
+
"target": "BatchCube",
|
|
151
|
+
"componentType": "Rigidbody"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"tool": "manage_gameobject",
|
|
156
|
+
"params": {
|
|
157
|
+
"action": "modify",
|
|
158
|
+
"target": "BatchCube",
|
|
159
|
+
"position": [0, 5, 0]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
json_output = json.dumps(template, indent=2)
|
|
165
|
+
|
|
166
|
+
if output:
|
|
167
|
+
with open(output, 'w') as f:
|
|
168
|
+
f.write(json_output)
|
|
169
|
+
print_success(f"Template written to: {output}")
|
|
170
|
+
else:
|
|
171
|
+
click.echo(json_output)
|