mcpforunityserver 9.3.0b20260128055651__py3-none-any.whl → 9.3.0b20260129121506__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/animation.py +6 -9
- cli/commands/asset.py +50 -80
- cli/commands/audio.py +14 -22
- cli/commands/batch.py +20 -33
- cli/commands/code.py +63 -70
- cli/commands/component.py +33 -55
- cli/commands/editor.py +122 -188
- cli/commands/gameobject.py +60 -83
- cli/commands/instance.py +28 -36
- cli/commands/lighting.py +54 -59
- cli/commands/material.py +39 -68
- cli/commands/prefab.py +63 -81
- cli/commands/scene.py +30 -54
- cli/commands/script.py +32 -50
- cli/commands/shader.py +43 -55
- cli/commands/texture.py +53 -51
- cli/commands/tool.py +24 -27
- cli/commands/ui.py +125 -130
- cli/commands/vfx.py +84 -138
- cli/utils/confirmation.py +37 -0
- cli/utils/connection.py +32 -2
- cli/utils/constants.py +23 -0
- cli/utils/parsers.py +112 -0
- core/config.py +0 -4
- core/telemetry.py +20 -2
- {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/METADATA +21 -1
- mcpforunityserver-9.3.0b20260129121506.dist-info/RECORD +103 -0
- services/resources/active_tool.py +1 -1
- services/resources/custom_tools.py +1 -1
- services/resources/editor_state.py +1 -1
- services/resources/gameobject.py +4 -4
- services/resources/layers.py +1 -1
- services/resources/menu_items.py +1 -1
- services/resources/prefab.py +3 -3
- services/resources/prefab_stage.py +1 -1
- services/resources/project_info.py +1 -1
- services/resources/selection.py +1 -1
- services/resources/tags.py +1 -1
- services/resources/tests.py +40 -8
- services/resources/unity_instances.py +1 -1
- services/resources/windows.py +1 -1
- services/tools/__init__.py +3 -1
- services/tools/find_gameobjects.py +32 -11
- services/tools/manage_gameobject.py +11 -66
- services/tools/manage_material.py +4 -37
- services/tools/manage_prefabs.py +51 -7
- services/tools/manage_script.py +1 -1
- services/tools/manage_texture.py +10 -96
- services/tools/run_tests.py +67 -4
- services/tools/utils.py +217 -0
- transport/models.py +1 -0
- transport/plugin_hub.py +2 -1
- transport/plugin_registry.py +3 -0
- transport/unity_transport.py +0 -51
- utils/focus_nudge.py +291 -23
- mcpforunityserver-9.3.0b20260128055651.dist-info/RECORD +0 -101
- utils/reload_sentinel.py +0 -9
- {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/WHEEL +0 -0
- {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/entry_points.txt +0 -0
- {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/licenses/LICENSE +0 -0
- {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/top_level.txt +0 -0
cli/commands/component.py
CHANGED
|
@@ -7,7 +7,10 @@ from typing import Optional, Any
|
|
|
7
7
|
|
|
8
8
|
from cli.utils.config import get_config
|
|
9
9
|
from cli.utils.output import format_output, print_error, print_success
|
|
10
|
-
from cli.utils.connection import run_command,
|
|
10
|
+
from cli.utils.connection import run_command, handle_unity_errors
|
|
11
|
+
from cli.utils.parsers import parse_value_safe, parse_json_dict_or_exit
|
|
12
|
+
from cli.utils.constants import SEARCH_METHOD_CHOICE_BASIC
|
|
13
|
+
from cli.utils.confirmation import confirm_destructive_action
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
@click.group()
|
|
@@ -21,7 +24,7 @@ def component():
|
|
|
21
24
|
@click.argument("component_type")
|
|
22
25
|
@click.option(
|
|
23
26
|
"--search-method",
|
|
24
|
-
type=
|
|
27
|
+
type=SEARCH_METHOD_CHOICE_BASIC,
|
|
25
28
|
default=None,
|
|
26
29
|
help="How to find the target GameObject."
|
|
27
30
|
)
|
|
@@ -30,6 +33,7 @@ def component():
|
|
|
30
33
|
default=None,
|
|
31
34
|
help='Initial properties as JSON (e.g., \'{"mass": 5.0}\').'
|
|
32
35
|
)
|
|
36
|
+
@handle_unity_errors
|
|
33
37
|
def add(target: str, component_type: str, search_method: Optional[str], properties: Optional[str]):
|
|
34
38
|
"""Add a component to a GameObject.
|
|
35
39
|
|
|
@@ -50,20 +54,12 @@ def add(target: str, component_type: str, search_method: Optional[str], properti
|
|
|
50
54
|
if search_method:
|
|
51
55
|
params["searchMethod"] = search_method
|
|
52
56
|
if properties:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
try:
|
|
60
|
-
result = run_command("manage_components", params, config)
|
|
61
|
-
click.echo(format_output(result, config.format))
|
|
62
|
-
if result.get("success"):
|
|
63
|
-
print_success(f"Added {component_type} to '{target}'")
|
|
64
|
-
except UnityConnectionError as e:
|
|
65
|
-
print_error(str(e))
|
|
66
|
-
sys.exit(1)
|
|
57
|
+
params["properties"] = parse_json_dict_or_exit(properties, "properties")
|
|
58
|
+
|
|
59
|
+
result = run_command("manage_components", params, config)
|
|
60
|
+
click.echo(format_output(result, config.format))
|
|
61
|
+
if result.get("success"):
|
|
62
|
+
print_success(f"Added {component_type} to '{target}'")
|
|
67
63
|
|
|
68
64
|
|
|
69
65
|
@component.command("remove")
|
|
@@ -71,7 +67,7 @@ def add(target: str, component_type: str, search_method: Optional[str], properti
|
|
|
71
67
|
@click.argument("component_type")
|
|
72
68
|
@click.option(
|
|
73
69
|
"--search-method",
|
|
74
|
-
type=
|
|
70
|
+
type=SEARCH_METHOD_CHOICE_BASIC,
|
|
75
71
|
default=None,
|
|
76
72
|
help="How to find the target GameObject."
|
|
77
73
|
)
|
|
@@ -80,6 +76,7 @@ def add(target: str, component_type: str, search_method: Optional[str], properti
|
|
|
80
76
|
is_flag=True,
|
|
81
77
|
help="Skip confirmation prompt."
|
|
82
78
|
)
|
|
79
|
+
@handle_unity_errors
|
|
83
80
|
def remove(target: str, component_type: str, search_method: Optional[str], force: bool):
|
|
84
81
|
"""Remove a component from a GameObject.
|
|
85
82
|
|
|
@@ -90,8 +87,7 @@ def remove(target: str, component_type: str, search_method: Optional[str], force
|
|
|
90
87
|
"""
|
|
91
88
|
config = get_config()
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
click.confirm(f"Remove {component_type} from '{target}'?", abort=True)
|
|
90
|
+
confirm_destructive_action("Remove", component_type, target, force, "from")
|
|
95
91
|
|
|
96
92
|
params: dict[str, Any] = {
|
|
97
93
|
"action": "remove",
|
|
@@ -102,14 +98,10 @@ def remove(target: str, component_type: str, search_method: Optional[str], force
|
|
|
102
98
|
if search_method:
|
|
103
99
|
params["searchMethod"] = search_method
|
|
104
100
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
print_success(f"Removed {component_type} from '{target}'")
|
|
110
|
-
except UnityConnectionError as e:
|
|
111
|
-
print_error(str(e))
|
|
112
|
-
sys.exit(1)
|
|
101
|
+
result = run_command("manage_components", params, config)
|
|
102
|
+
click.echo(format_output(result, config.format))
|
|
103
|
+
if result.get("success"):
|
|
104
|
+
print_success(f"Removed {component_type} from '{target}'")
|
|
113
105
|
|
|
114
106
|
|
|
115
107
|
@component.command("set")
|
|
@@ -119,10 +111,11 @@ def remove(target: str, component_type: str, search_method: Optional[str], force
|
|
|
119
111
|
@click.argument("value")
|
|
120
112
|
@click.option(
|
|
121
113
|
"--search-method",
|
|
122
|
-
type=
|
|
114
|
+
type=SEARCH_METHOD_CHOICE_BASIC,
|
|
123
115
|
default=None,
|
|
124
116
|
help="How to find the target GameObject."
|
|
125
117
|
)
|
|
118
|
+
@handle_unity_errors
|
|
126
119
|
def set_property(target: str, component_type: str, property_name: str, value: str, search_method: Optional[str]):
|
|
127
120
|
"""Set a single property on a component.
|
|
128
121
|
|
|
@@ -135,11 +128,7 @@ def set_property(target: str, component_type: str, property_name: str, value: st
|
|
|
135
128
|
config = get_config()
|
|
136
129
|
|
|
137
130
|
# Try to parse value as JSON for complex types
|
|
138
|
-
|
|
139
|
-
parsed_value = json.loads(value)
|
|
140
|
-
except json.JSONDecodeError:
|
|
141
|
-
# Keep as string if not valid JSON
|
|
142
|
-
parsed_value = value
|
|
131
|
+
parsed_value = parse_value_safe(value)
|
|
143
132
|
|
|
144
133
|
params: dict[str, Any] = {
|
|
145
134
|
"action": "set_property",
|
|
@@ -152,14 +141,10 @@ def set_property(target: str, component_type: str, property_name: str, value: st
|
|
|
152
141
|
if search_method:
|
|
153
142
|
params["searchMethod"] = search_method
|
|
154
143
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
print_success(f"Set {component_type}.{property_name} = {value}")
|
|
160
|
-
except UnityConnectionError as e:
|
|
161
|
-
print_error(str(e))
|
|
162
|
-
sys.exit(1)
|
|
144
|
+
result = run_command("manage_components", params, config)
|
|
145
|
+
click.echo(format_output(result, config.format))
|
|
146
|
+
if result.get("success"):
|
|
147
|
+
print_success(f"Set {component_type}.{property_name} = {value}")
|
|
163
148
|
|
|
164
149
|
|
|
165
150
|
@component.command("modify")
|
|
@@ -172,10 +157,11 @@ def set_property(target: str, component_type: str, property_name: str, value: st
|
|
|
172
157
|
)
|
|
173
158
|
@click.option(
|
|
174
159
|
"--search-method",
|
|
175
|
-
type=
|
|
160
|
+
type=SEARCH_METHOD_CHOICE_BASIC,
|
|
176
161
|
default=None,
|
|
177
162
|
help="How to find the target GameObject."
|
|
178
163
|
)
|
|
164
|
+
@handle_unity_errors
|
|
179
165
|
def modify(target: str, component_type: str, properties: str, search_method: Optional[str]):
|
|
180
166
|
"""Set multiple properties on a component at once.
|
|
181
167
|
|
|
@@ -186,11 +172,7 @@ def modify(target: str, component_type: str, properties: str, search_method: Opt
|
|
|
186
172
|
"""
|
|
187
173
|
config = get_config()
|
|
188
174
|
|
|
189
|
-
|
|
190
|
-
props_dict = json.loads(properties)
|
|
191
|
-
except json.JSONDecodeError as e:
|
|
192
|
-
print_error(f"Invalid JSON for properties: {e}")
|
|
193
|
-
sys.exit(1)
|
|
175
|
+
props_dict = parse_json_dict_or_exit(properties, "properties")
|
|
194
176
|
|
|
195
177
|
params: dict[str, Any] = {
|
|
196
178
|
"action": "set_property",
|
|
@@ -202,11 +184,7 @@ def modify(target: str, component_type: str, properties: str, search_method: Opt
|
|
|
202
184
|
if search_method:
|
|
203
185
|
params["searchMethod"] = search_method
|
|
204
186
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
print_success(f"Modified {component_type} on '{target}'")
|
|
210
|
-
except UnityConnectionError as e:
|
|
211
|
-
print_error(str(e))
|
|
212
|
-
sys.exit(1)
|
|
187
|
+
result = run_command("manage_components", params, config)
|
|
188
|
+
click.echo(format_output(result, config.format))
|
|
189
|
+
if result.get("success"):
|
|
190
|
+
print_success(f"Modified {component_type} on '{target}'")
|
cli/commands/editor.py
CHANGED
|
@@ -6,8 +6,9 @@ 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, run_list_custom_tools, UnityConnectionError
|
|
9
|
+
from cli.utils.connection import run_command, run_list_custom_tools, handle_unity_errors, UnityConnectionError
|
|
10
10
|
from cli.utils.suggestions import suggest_matches, format_suggestions
|
|
11
|
+
from cli.utils.parsers import parse_json_dict_or_exit
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
@click.group()
|
|
@@ -17,48 +18,36 @@ def editor():
|
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
@editor.command("play")
|
|
21
|
+
@handle_unity_errors
|
|
20
22
|
def play():
|
|
21
23
|
"""Enter play mode."""
|
|
22
24
|
config = get_config()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if result.get("success"):
|
|
28
|
-
print_success("Entered play mode")
|
|
29
|
-
except UnityConnectionError as e:
|
|
30
|
-
print_error(str(e))
|
|
31
|
-
sys.exit(1)
|
|
25
|
+
result = run_command("manage_editor", {"action": "play"}, config)
|
|
26
|
+
click.echo(format_output(result, config.format))
|
|
27
|
+
if result.get("success"):
|
|
28
|
+
print_success("Entered play mode")
|
|
32
29
|
|
|
33
30
|
|
|
34
31
|
@editor.command("pause")
|
|
32
|
+
@handle_unity_errors
|
|
35
33
|
def pause():
|
|
36
34
|
"""Pause play mode."""
|
|
37
35
|
config = get_config()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if result.get("success"):
|
|
43
|
-
print_success("Paused play mode")
|
|
44
|
-
except UnityConnectionError as e:
|
|
45
|
-
print_error(str(e))
|
|
46
|
-
sys.exit(1)
|
|
36
|
+
result = run_command("manage_editor", {"action": "pause"}, config)
|
|
37
|
+
click.echo(format_output(result, config.format))
|
|
38
|
+
if result.get("success"):
|
|
39
|
+
print_success("Paused play mode")
|
|
47
40
|
|
|
48
41
|
|
|
49
42
|
@editor.command("stop")
|
|
43
|
+
@handle_unity_errors
|
|
50
44
|
def stop():
|
|
51
45
|
"""Stop play mode."""
|
|
52
46
|
config = get_config()
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if result.get("success"):
|
|
58
|
-
print_success("Stopped play mode")
|
|
59
|
-
except UnityConnectionError as e:
|
|
60
|
-
print_error(str(e))
|
|
61
|
-
sys.exit(1)
|
|
47
|
+
result = run_command("manage_editor", {"action": "stop"}, config)
|
|
48
|
+
click.echo(format_output(result, config.format))
|
|
49
|
+
if result.get("success"):
|
|
50
|
+
print_success("Stopped play mode")
|
|
62
51
|
|
|
63
52
|
|
|
64
53
|
@editor.command("console")
|
|
@@ -92,6 +81,7 @@ def stop():
|
|
|
92
81
|
is_flag=True,
|
|
93
82
|
help="Clear the console instead of reading."
|
|
94
83
|
)
|
|
84
|
+
@handle_unity_errors
|
|
95
85
|
def console(log_types: tuple, count: int, filter_text: Optional[str], stacktrace: bool, clear: bool):
|
|
96
86
|
"""Read or clear the Unity console.
|
|
97
87
|
|
|
@@ -105,14 +95,10 @@ def console(log_types: tuple, count: int, filter_text: Optional[str], stacktrace
|
|
|
105
95
|
config = get_config()
|
|
106
96
|
|
|
107
97
|
if clear:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
print_success("Console cleared")
|
|
113
|
-
except UnityConnectionError as e:
|
|
114
|
-
print_error(str(e))
|
|
115
|
-
sys.exit(1)
|
|
98
|
+
result = run_command("read_console", {"action": "clear"}, config)
|
|
99
|
+
click.echo(format_output(result, config.format))
|
|
100
|
+
if result.get("success"):
|
|
101
|
+
print_success("Console cleared")
|
|
116
102
|
return
|
|
117
103
|
|
|
118
104
|
params: dict[str, Any] = {
|
|
@@ -125,16 +111,13 @@ def console(log_types: tuple, count: int, filter_text: Optional[str], stacktrace
|
|
|
125
111
|
if filter_text:
|
|
126
112
|
params["filter_text"] = filter_text
|
|
127
113
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
click.echo(format_output(result, config.format))
|
|
131
|
-
except UnityConnectionError as e:
|
|
132
|
-
print_error(str(e))
|
|
133
|
-
sys.exit(1)
|
|
114
|
+
result = run_command("read_console", params, config)
|
|
115
|
+
click.echo(format_output(result, config.format))
|
|
134
116
|
|
|
135
117
|
|
|
136
118
|
@editor.command("add-tag")
|
|
137
119
|
@click.argument("tag_name")
|
|
120
|
+
@handle_unity_errors
|
|
138
121
|
def add_tag(tag_name: str):
|
|
139
122
|
"""Add a new tag.
|
|
140
123
|
|
|
@@ -144,20 +127,16 @@ def add_tag(tag_name: str):
|
|
|
144
127
|
unity-mcp editor add-tag "Collectible"
|
|
145
128
|
"""
|
|
146
129
|
config = get_config()
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if result.get("success"):
|
|
153
|
-
print_success(f"Added tag: {tag_name}")
|
|
154
|
-
except UnityConnectionError as e:
|
|
155
|
-
print_error(str(e))
|
|
156
|
-
sys.exit(1)
|
|
130
|
+
result = run_command(
|
|
131
|
+
"manage_editor", {"action": "add_tag", "tagName": tag_name}, config)
|
|
132
|
+
click.echo(format_output(result, config.format))
|
|
133
|
+
if result.get("success"):
|
|
134
|
+
print_success(f"Added tag: {tag_name}")
|
|
157
135
|
|
|
158
136
|
|
|
159
137
|
@editor.command("remove-tag")
|
|
160
138
|
@click.argument("tag_name")
|
|
139
|
+
@handle_unity_errors
|
|
161
140
|
def remove_tag(tag_name: str):
|
|
162
141
|
"""Remove a tag.
|
|
163
142
|
|
|
@@ -166,20 +145,16 @@ def remove_tag(tag_name: str):
|
|
|
166
145
|
unity-mcp editor remove-tag "OldTag"
|
|
167
146
|
"""
|
|
168
147
|
config = get_config()
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if result.get("success"):
|
|
175
|
-
print_success(f"Removed tag: {tag_name}")
|
|
176
|
-
except UnityConnectionError as e:
|
|
177
|
-
print_error(str(e))
|
|
178
|
-
sys.exit(1)
|
|
148
|
+
result = run_command(
|
|
149
|
+
"manage_editor", {"action": "remove_tag", "tagName": tag_name}, config)
|
|
150
|
+
click.echo(format_output(result, config.format))
|
|
151
|
+
if result.get("success"):
|
|
152
|
+
print_success(f"Removed tag: {tag_name}")
|
|
179
153
|
|
|
180
154
|
|
|
181
155
|
@editor.command("add-layer")
|
|
182
156
|
@click.argument("layer_name")
|
|
157
|
+
@handle_unity_errors
|
|
183
158
|
def add_layer(layer_name: str):
|
|
184
159
|
"""Add a new layer.
|
|
185
160
|
|
|
@@ -188,20 +163,16 @@ def add_layer(layer_name: str):
|
|
|
188
163
|
unity-mcp editor add-layer "Interactable"
|
|
189
164
|
"""
|
|
190
165
|
config = get_config()
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if result.get("success"):
|
|
197
|
-
print_success(f"Added layer: {layer_name}")
|
|
198
|
-
except UnityConnectionError as e:
|
|
199
|
-
print_error(str(e))
|
|
200
|
-
sys.exit(1)
|
|
166
|
+
result = run_command(
|
|
167
|
+
"manage_editor", {"action": "add_layer", "layerName": layer_name}, config)
|
|
168
|
+
click.echo(format_output(result, config.format))
|
|
169
|
+
if result.get("success"):
|
|
170
|
+
print_success(f"Added layer: {layer_name}")
|
|
201
171
|
|
|
202
172
|
|
|
203
173
|
@editor.command("remove-layer")
|
|
204
174
|
@click.argument("layer_name")
|
|
175
|
+
@handle_unity_errors
|
|
205
176
|
def remove_layer(layer_name: str):
|
|
206
177
|
"""Remove a layer.
|
|
207
178
|
|
|
@@ -210,20 +181,16 @@ def remove_layer(layer_name: str):
|
|
|
210
181
|
unity-mcp editor remove-layer "OldLayer"
|
|
211
182
|
"""
|
|
212
183
|
config = get_config()
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if result.get("success"):
|
|
219
|
-
print_success(f"Removed layer: {layer_name}")
|
|
220
|
-
except UnityConnectionError as e:
|
|
221
|
-
print_error(str(e))
|
|
222
|
-
sys.exit(1)
|
|
184
|
+
result = run_command(
|
|
185
|
+
"manage_editor", {"action": "remove_layer", "layerName": layer_name}, config)
|
|
186
|
+
click.echo(format_output(result, config.format))
|
|
187
|
+
if result.get("success"):
|
|
188
|
+
print_success(f"Removed layer: {layer_name}")
|
|
223
189
|
|
|
224
190
|
|
|
225
191
|
@editor.command("tool")
|
|
226
192
|
@click.argument("tool_name")
|
|
193
|
+
@handle_unity_errors
|
|
227
194
|
def set_tool(tool_name: str):
|
|
228
195
|
"""Set the active editor tool.
|
|
229
196
|
|
|
@@ -234,20 +201,16 @@ def set_tool(tool_name: str):
|
|
|
234
201
|
unity-mcp editor tool "Scale"
|
|
235
202
|
"""
|
|
236
203
|
config = get_config()
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if result.get("success"):
|
|
243
|
-
print_success(f"Set active tool: {tool_name}")
|
|
244
|
-
except UnityConnectionError as e:
|
|
245
|
-
print_error(str(e))
|
|
246
|
-
sys.exit(1)
|
|
204
|
+
result = run_command(
|
|
205
|
+
"manage_editor", {"action": "set_active_tool", "toolName": tool_name}, config)
|
|
206
|
+
click.echo(format_output(result, config.format))
|
|
207
|
+
if result.get("success"):
|
|
208
|
+
print_success(f"Set active tool: {tool_name}")
|
|
247
209
|
|
|
248
210
|
|
|
249
211
|
@editor.command("menu")
|
|
250
212
|
@click.argument("menu_path")
|
|
213
|
+
@handle_unity_errors
|
|
251
214
|
def execute_menu(menu_path: str):
|
|
252
215
|
"""Execute a menu item.
|
|
253
216
|
|
|
@@ -258,16 +221,10 @@ def execute_menu(menu_path: str):
|
|
|
258
221
|
unity-mcp editor menu "GameObject/Create Empty"
|
|
259
222
|
"""
|
|
260
223
|
config = get_config()
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
click.echo(format_output(result, config.format))
|
|
266
|
-
if result.get("success"):
|
|
267
|
-
print_success(f"Executed: {menu_path}")
|
|
268
|
-
except UnityConnectionError as e:
|
|
269
|
-
print_error(str(e))
|
|
270
|
-
sys.exit(1)
|
|
224
|
+
result = run_command("execute_menu_item", {"menu_path": menu_path}, config)
|
|
225
|
+
click.echo(format_output(result, config.format))
|
|
226
|
+
if result.get("success"):
|
|
227
|
+
print_success(f"Executed: {menu_path}")
|
|
271
228
|
|
|
272
229
|
|
|
273
230
|
@editor.command("tests")
|
|
@@ -298,6 +255,7 @@ def execute_menu(menu_path: str):
|
|
|
298
255
|
is_flag=True,
|
|
299
256
|
help="Include details for failed/skipped tests only."
|
|
300
257
|
)
|
|
258
|
+
@handle_unity_errors
|
|
301
259
|
def run_tests(mode: str, async_mode: bool, wait: Optional[int], details: bool, failed_only: bool):
|
|
302
260
|
"""Run Unity tests.
|
|
303
261
|
|
|
@@ -318,21 +276,17 @@ def run_tests(mode: str, async_mode: bool, wait: Optional[int], details: bool, f
|
|
|
318
276
|
if failed_only:
|
|
319
277
|
params["include_failed_tests"] = True
|
|
320
278
|
|
|
321
|
-
|
|
322
|
-
result = run_command("run_tests", params, config)
|
|
279
|
+
result = run_command("run_tests", params, config)
|
|
323
280
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
281
|
+
# For async mode, just show job ID
|
|
282
|
+
if async_mode and result.get("success"):
|
|
283
|
+
job_id = result.get("data", {}).get("job_id")
|
|
284
|
+
if job_id:
|
|
285
|
+
click.echo(f"Test job started: {job_id}")
|
|
286
|
+
print_info("Poll with: unity-mcp editor poll-test " + job_id)
|
|
287
|
+
return
|
|
331
288
|
|
|
332
|
-
|
|
333
|
-
except UnityConnectionError as e:
|
|
334
|
-
print_error(str(e))
|
|
335
|
-
sys.exit(1)
|
|
289
|
+
click.echo(format_output(result, config.format))
|
|
336
290
|
|
|
337
291
|
|
|
338
292
|
@editor.command("poll-test")
|
|
@@ -353,6 +307,7 @@ def run_tests(mode: str, async_mode: bool, wait: Optional[int], details: bool, f
|
|
|
353
307
|
is_flag=True,
|
|
354
308
|
help="Include details for failed/skipped tests only."
|
|
355
309
|
)
|
|
310
|
+
@handle_unity_errors
|
|
356
311
|
def poll_test(job_id: str, wait: int, details: bool, failed_only: bool):
|
|
357
312
|
"""Poll an async test job for status/results.
|
|
358
313
|
|
|
@@ -372,27 +327,23 @@ def poll_test(job_id: str, wait: int, details: bool, failed_only: bool):
|
|
|
372
327
|
if failed_only:
|
|
373
328
|
params["include_failed_tests"] = True
|
|
374
329
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
print_info(f"Tests running: {completed}/{total}")
|
|
393
|
-
except UnityConnectionError as e:
|
|
394
|
-
print_error(str(e))
|
|
395
|
-
sys.exit(1)
|
|
330
|
+
result = run_command("get_test_job", params, config)
|
|
331
|
+
click.echo(format_output(result, config.format))
|
|
332
|
+
|
|
333
|
+
if isinstance(result, dict) and result.get("success"):
|
|
334
|
+
data = result.get("data", {})
|
|
335
|
+
status = data.get("status", "unknown")
|
|
336
|
+
if status == "succeeded":
|
|
337
|
+
print_success("Tests completed successfully")
|
|
338
|
+
elif status == "failed":
|
|
339
|
+
summary = data.get("result", {}).get("summary", {})
|
|
340
|
+
failed = summary.get("failed", 0)
|
|
341
|
+
print_error(f"Tests failed: {failed} failures")
|
|
342
|
+
elif status == "running":
|
|
343
|
+
progress = data.get("progress", {})
|
|
344
|
+
completed = progress.get("completed", 0)
|
|
345
|
+
total = progress.get("total", 0)
|
|
346
|
+
print_info(f"Tests running: {completed}/{total}")
|
|
396
347
|
|
|
397
348
|
|
|
398
349
|
@editor.command("refresh")
|
|
@@ -418,6 +369,7 @@ def poll_test(job_id: str, wait: int, details: bool, failed_only: bool):
|
|
|
418
369
|
is_flag=True,
|
|
419
370
|
help="Don't wait for refresh to complete."
|
|
420
371
|
)
|
|
372
|
+
@handle_unity_errors
|
|
421
373
|
def refresh(mode: str, scope: str, compile: bool, no_wait: bool):
|
|
422
374
|
"""Force Unity to refresh assets/scripts.
|
|
423
375
|
|
|
@@ -438,15 +390,11 @@ def refresh(mode: str, scope: str, compile: bool, no_wait: bool):
|
|
|
438
390
|
if compile:
|
|
439
391
|
params["compile"] = "request"
|
|
440
392
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
print_success("Unity refreshed")
|
|
447
|
-
except UnityConnectionError as e:
|
|
448
|
-
print_error(str(e))
|
|
449
|
-
sys.exit(1)
|
|
393
|
+
click.echo("Refreshing Unity...")
|
|
394
|
+
result = run_command("refresh_unity", params, config)
|
|
395
|
+
click.echo(format_output(result, config.format))
|
|
396
|
+
if result.get("success"):
|
|
397
|
+
print_success("Unity refreshed")
|
|
450
398
|
|
|
451
399
|
|
|
452
400
|
@editor.command("custom-tool")
|
|
@@ -456,6 +404,7 @@ def refresh(mode: str, scope: str, compile: bool, no_wait: bool):
|
|
|
456
404
|
default="{}",
|
|
457
405
|
help="Tool parameters as JSON."
|
|
458
406
|
)
|
|
407
|
+
@handle_unity_errors
|
|
459
408
|
def custom_tool(tool_name: str, params: str):
|
|
460
409
|
"""Execute a custom Unity tool.
|
|
461
410
|
|
|
@@ -466,48 +415,33 @@ def custom_tool(tool_name: str, params: str):
|
|
|
466
415
|
unity-mcp editor custom-tool "MyCustomTool"
|
|
467
416
|
unity-mcp editor custom-tool "BuildPipeline" --params '{"target": "Android"}'
|
|
468
417
|
"""
|
|
469
|
-
import json
|
|
470
418
|
config = get_config()
|
|
471
419
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
|
511
|
-
except UnityConnectionError as e:
|
|
512
|
-
print_error(str(e))
|
|
513
|
-
sys.exit(1)
|
|
420
|
+
params_dict = parse_json_dict_or_exit(params, "params")
|
|
421
|
+
|
|
422
|
+
result = run_command("execute_custom_tool", {
|
|
423
|
+
"tool_name": tool_name,
|
|
424
|
+
"parameters": params_dict,
|
|
425
|
+
}, config)
|
|
426
|
+
click.echo(format_output(result, config.format))
|
|
427
|
+
if result.get("success"):
|
|
428
|
+
print_success(f"Executed custom tool: {tool_name}")
|
|
429
|
+
else:
|
|
430
|
+
message = (result.get("message") or result.get("error") or "").lower()
|
|
431
|
+
if "not found" in message and "tool" in message:
|
|
432
|
+
try:
|
|
433
|
+
tools_result = run_list_custom_tools(config)
|
|
434
|
+
tools = tools_result.get("tools")
|
|
435
|
+
if tools is None:
|
|
436
|
+
data = tools_result.get("data", {})
|
|
437
|
+
tools = data.get("tools") if isinstance(data, dict) else None
|
|
438
|
+
names = [
|
|
439
|
+
t.get("name") for t in tools if isinstance(t, dict) and t.get("name")
|
|
440
|
+
] if isinstance(tools, list) else []
|
|
441
|
+
matches = suggest_matches(tool_name, names)
|
|
442
|
+
suggestion = format_suggestions(matches)
|
|
443
|
+
if suggestion:
|
|
444
|
+
print_info(suggestion)
|
|
445
|
+
print_info(f'Example: unity-mcp editor custom-tool "{matches[0]}"')
|
|
446
|
+
except UnityConnectionError:
|
|
447
|
+
pass
|