Skip to content

Commit 3cd68d9

Browse files
committed
fix: harden parameter type handling and resolve PR feedback
Parameter Type Improvements: - Broaden count in read_console.py to accept int | str - Broaden build_index in manage_scene.py to accept int | str - Harden vector parsing in manage_gameobject.py with NaN/Inf checks - Add whitespace-delimited vector support (e.g., "1 2 3") - Narrow exception handling from Exception to (ValueError, TypeError) Test Improvements: - Harden _load_module in test files with spec/loader validation - Fix test_manage_gameobject_boolean_and_tag_mapping by mapping tag→search_term Bug Fixes: - Fix syntax error in manage_shader.py (remove stray 'x') Version: Bump to 6.2.3 All tests pass: 41 passed, 5 skipped, 7 xpassed
1 parent c0cd911 commit 3cd68d9

12 files changed

+32
-14
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.2.2
1+
6.2.3

MCPForUnity/UnityMcpServer~/src/tools/manage_gameobject.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,24 @@ def _coerce_bool(value, default=None):
8383
def _coerce_vec(value, default=None):
8484
if value is None:
8585
return default
86-
if isinstance(value, list) and len(value) == 3:
86+
import math
87+
def _to_vec3(parts):
8788
try:
88-
return [float(value[0]), float(value[1]), float(value[2])]
89-
except Exception:
89+
vec = [float(parts[0]), float(parts[1]), float(parts[2])]
90+
except (ValueError, TypeError):
9091
return default
92+
return vec if all(math.isfinite(n) for n in vec) else default
93+
if isinstance(value, list) and len(value) == 3:
94+
return _to_vec3(value)
9195
if isinstance(value, str):
9296
s = value.strip()
9397
# minimal tolerant parse for "[x,y,z]" or "x,y,z"
9498
if s.startswith("[") and s.endswith("]"):
9599
s = s[1:-1]
96-
parts = [p.strip() for p in s.split(",")]
100+
# support "x,y,z" and "x y z"
101+
parts = [p.strip() for p in (s.split(",") if "," in s else s.split())]
97102
if len(parts) == 3:
98-
try:
99-
return [float(parts[0]), float(parts[1]), float(parts[2])]
100-
except Exception:
101-
return default
103+
return _to_vec3(parts)
102104
return default
103105

104106
position = _coerce_vec(position, default=position)
@@ -112,6 +114,10 @@ def _coerce_vec(value, default=None):
112114
includeNonPublicSerialized = _coerce_bool(includeNonPublicSerialized)
113115

114116
try:
117+
# Map tag to search_term when search_method is by_tag for backward compatibility
118+
if action == "find" and search_method == "by_tag" and tag is not None and search_term is None:
119+
search_term = tag
120+
115121
# Validate parameter usage to prevent silent failures
116122
if action == "find":
117123
if name is not None:

MCPForUnity/UnityMcpServer~/src/tools/manage_scene.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ def manage_scene(
1313
"Scene name. Not required get_active/get_build_settings"] | None = None,
1414
path: Annotated[str,
1515
"Asset path for scene operations (default: 'Assets/')"] | None = None,
16-
build_index: Annotated[str,
17-
"Build index for load/build settings actions (pass as quoted string, e.g., '0')"] | None = None,
16+
build_index: Annotated[int | str,
17+
"Build index for load/build settings actions (accepts int or string, e.g., 0 or '0')"] | None = None,
1818
) -> dict[str, Any]:
1919
ctx.info(f"Processing manage_scene: {action}")
2020
try:

MCPForUnity/UnityMcpServer~/src/tools/manage_shader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
description="Manages shader scripts in Unity (create, read, update, delete)."
1111
)
1212
def manage_shader(
13-
ctx: Context,x
13+
ctx: Context,
1414
action: Annotated[Literal['create', 'read', 'update', 'delete'], "Perform CRUD operations on shader scripts."],
1515
name: Annotated[str, "Shader name (no .cs extension)"],
1616
path: Annotated[str, "Asset path (default: \"Assets/\")"],

MCPForUnity/UnityMcpServer~/src/tools/read_console.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def read_console(
1616
action: Annotated[Literal['get', 'clear'], "Get or clear the Unity Editor console."] | None = None,
1717
types: Annotated[list[Literal['error', 'warning',
1818
'log', 'all']], "Message types to get"] | None = None,
19-
count: Annotated[str, "Max messages to return (pass as quoted string, e.g., '5')"] | None = None,
19+
count: Annotated[int | str, "Max messages to return (accepts int or string, e.g., 5 or '5')"] | None = None,
2020
filter_text: Annotated[str, "Text filter for messages"] | None = None,
2121
since_timestamp: Annotated[str,
2222
"Get messages after this timestamp (ISO 8601)"] | None = None,

MCPForUnity/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "com.coplaydev.unity-mcp",
3-
"version": "6.2.2",
3+
"version": "6.2.3",
44
"displayName": "MCP for Unity",
55
"description": "A bridge that connects AI assistants to Unity via the MCP (Model Context Protocol). Allows AI clients like Claude Code, Cursor, and VSCode to directly control your Unity Editor for enhanced development workflows.\n\nFeatures automated setup wizard, cross-platform support, and seamless integration with popular AI development tools.\n\nJoin Our Discord: https://discord.gg/y4p8KfzrN4",
66
"unity": "2021.3",

tests/test_get_sha.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
def _load_module(path: pathlib.Path, name: str):
1212
spec = importlib.util.spec_from_file_location(name, path)
13+
if spec is None or spec.loader is None:
14+
raise ImportError(f"Cannot load module {name} from {path}")
1315
mod = importlib.util.module_from_spec(spec)
1416
spec.loader.exec_module(mod)
1517
return mod

tests/test_manage_asset_param_coercion.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
def _load_module(path: pathlib.Path, name: str):
1414
spec = importlib.util.spec_from_file_location(name, path)
15+
if spec is None or spec.loader is None:
16+
raise ImportError(f"Cannot load module {name} from {path}")
1517
mod = importlib.util.module_from_spec(spec)
1618
spec.loader.exec_module(mod)
1719
return mod

tests/test_manage_gameobject_param_coercion.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
def _load_module(path: pathlib.Path, name: str):
1313
spec = importlib.util.spec_from_file_location(name, path)
14+
if spec is None or spec.loader is None:
15+
raise ImportError(f"Cannot load module {name} from {path}")
1416
mod = importlib.util.module_from_spec(spec)
1517
spec.loader.exec_module(mod)
1618
return mod

tests/test_read_console_truncate.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
def _load_module(path: pathlib.Path, name: str):
1111
spec = importlib.util.spec_from_file_location(name, path)
12+
if spec is None or spec.loader is None:
13+
raise ImportError(f"Cannot load module {name} from {path}")
1214
mod = importlib.util.module_from_spec(spec)
1315
spec.loader.exec_module(mod)
1416
return mod

0 commit comments

Comments
 (0)