unity-mcp/tests/test_manage_gameobject_para...

72 lines
2.0 KiB
Python
Raw Normal View History

Harden MCP tool parameter handling to eliminate “invalid param” errors (#339) * feat: migrate to FastMCP 2.0 (v2.12.5) - Update pyproject.toml to use fastmcp>=2.12.5 instead of mcp[cli] - Replace all imports from mcp.server.fastmcp to fastmcp - Maintain MCP protocol compliance with mcp>=1.16.0 - All 15 files updated with new import statements - Server and tools registration working with FastMCP 2.0 * chore: bump MCP for Unity to 6.2.2 and widen numeric tool params (asset search/read_resource/run_tests) for better LLM compatibility * chore: bump installed server_version.txt to 6.2.2 so Unity installer logs correct version * fix(parameters): apply parameter hardening to read_console, manage_editor, and manage_gameobject - read_console: accept int|str for count parameter with coercion - manage_editor: accept bool|str for wait_for_completion with coercion - manage_gameobject: accept bool|str for all boolean parameters with coercion - All tools now handle string parameters gracefully and convert to proper types internally * chore(deps): drop fastmcp, use mcp>=1.18.0; update imports to mcp.server.fastmcp * chore(deps): re-add fastmcp>=2.12.5, relax mcp to >=1.16.0 Adds fastmcp as explicit dependency for FastMCP 2.0 migration. Relaxes mcp version constraint to support broader compatibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: remove obsolete mcp stubs for FastMCP 2.0 compatibility Removes stub mcp modules from test files that were conflicting with the real mcp and fastmcp packages now installed as dependencies. Adds tests/__init__.py to make tests a proper Python package. This fixes test collection errors after migrating to FastMCP 2.0. Test results: 40 passed, 7 xpassed, 5 skipped, 1 failed (pre-existing) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: complete FastMCP 2.0 migration with correct import paths Updates all remaining files to use `from fastmcp import` instead of the old `from mcp.server.fastmcp import` path. Changes: - server.py: Update FastMCP import - tools/__init__.py: Update FastMCP import - resources/__init__.py: Update FastMCP import - tools/manage_script.py, read_console.py, resource_tools.py: Update imports - test stubs: Update to stub `fastmcp` instead of `mcp.server.fastmcp` Addresses PR review feedback about incomplete migration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * 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 --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-10-23 09:42:46 +08:00
import sys
import pathlib
import importlib.util
import types
import os
ROOT = pathlib.Path(__file__).resolve().parents[1]
SRC = ROOT / "MCPForUnity" / "UnityMcpServer~" / "src"
sys.path.insert(0, str(SRC))
def _load_module(path: pathlib.Path, name: str):
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise ImportError(f"Cannot load module {name} from {path}")
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
# Stub fastmcp to avoid real MCP deps
fastmcp_pkg = types.ModuleType("fastmcp")
class _Dummy:
pass
fastmcp_pkg.FastMCP = _Dummy
fastmcp_pkg.Context = _Dummy
sys.modules.setdefault("fastmcp", fastmcp_pkg)
from tests.test_helpers import DummyContext
def test_manage_gameobject_boolean_and_tag_mapping(monkeypatch):
# Import with SRC as CWD to satisfy telemetry import side effects
_prev = os.getcwd()
os.chdir(str(SRC))
try:
manage_go_mod = _load_module(SRC / "tools" / "manage_gameobject.py", "manage_go_mod")
finally:
os.chdir(_prev)
captured = {}
def fake_send(cmd, params):
captured["params"] = params
return {"success": True, "data": {}}
monkeypatch.setattr(manage_go_mod, "send_command_with_retry", fake_send)
# find by tag: allow tag to map to searchTerm
resp = manage_go_mod.manage_gameobject(
ctx=DummyContext(),
action="find",
search_method="by_tag",
tag="Player",
find_all="true",
search_inactive="0",
)
# Loosen equality: wrapper may include a diagnostic message
assert resp.get("success") is True
assert "data" in resp
# ensure tag mapped to searchTerm and booleans passed through; C# side coerces true/false already
assert captured["params"]["searchTerm"] == "Player"
assert captured["params"]["findAll"] == "true" or captured["params"]["findAll"] is True
assert captured["params"]["searchInactive"] in ("0", False, 0)