dcc-mcp-unity 0.2.0__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.
Files changed (34) hide show
  1. dcc_mcp_unity/__init__.py +6 -0
  2. dcc_mcp_unity/__version__.py +3 -0
  3. dcc_mcp_unity/bridge.py +64 -0
  4. dcc_mcp_unity/dispatcher.py +21 -0
  5. dcc_mcp_unity/install.py +67 -0
  6. dcc_mcp_unity/server.py +101 -0
  7. dcc_mcp_unity/skills/unity-diagnostics/SKILL.md +24 -0
  8. dcc_mcp_unity/skills/unity-diagnostics/metadata/depends.md +3 -0
  9. dcc_mcp_unity/skills/unity-diagnostics/scripts/read_console.py +16 -0
  10. dcc_mcp_unity/skills/unity-diagnostics/tools.yaml +21 -0
  11. dcc_mcp_unity/skills/unity-project/SKILL.md +27 -0
  12. dcc_mcp_unity/skills/unity-project/metadata/depends.md +3 -0
  13. dcc_mcp_unity/skills/unity-project/scripts/inspect_project.py +14 -0
  14. dcc_mcp_unity/skills/unity-project/scripts/refresh_assets.py +14 -0
  15. dcc_mcp_unity/skills/unity-project/tools.yaml +31 -0
  16. dcc_mcp_unity/skills/unity-scene/SKILL.md +27 -0
  17. dcc_mcp_unity/skills/unity-scene/metadata/depends.md +3 -0
  18. dcc_mcp_unity/skills/unity-scene/scripts/create_game_object.py +18 -0
  19. dcc_mcp_unity/skills/unity-scene/scripts/inspect_scene.py +15 -0
  20. dcc_mcp_unity/skills/unity-scene/scripts/save_scene.py +14 -0
  21. dcc_mcp_unity/skills/unity-scene/scripts/set_transform.py +31 -0
  22. dcc_mcp_unity/skills/unity-scene/tools.yaml +95 -0
  23. dcc_mcp_unity/unity_package/Editor/DccMcp.Unity.Editor.asmdef +9 -0
  24. dcc_mcp_unity/unity_package/Editor/DccMcpBridge.cs +358 -0
  25. dcc_mcp_unity/unity_package/Editor/DccMcpCommands.cs +406 -0
  26. dcc_mcp_unity/unity_package/Editor/DccMcpConsole.cs +200 -0
  27. dcc_mcp_unity/unity_package/LICENSE.md +21 -0
  28. dcc_mcp_unity/unity_package/README.md +7 -0
  29. dcc_mcp_unity/unity_package/package.json +18 -0
  30. dcc_mcp_unity-0.2.0.dist-info/METADATA +105 -0
  31. dcc_mcp_unity-0.2.0.dist-info/RECORD +34 -0
  32. dcc_mcp_unity-0.2.0.dist-info/WHEEL +4 -0
  33. dcc_mcp_unity-0.2.0.dist-info/entry_points.txt +6 -0
  34. dcc_mcp_unity-0.2.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,6 @@
1
+ """Unity Editor adapter for DCC-MCP."""
2
+
3
+ from .__version__ import __version__
4
+ from .server import UnityMcpServer
5
+
6
+ __all__ = ["UnityMcpServer", "__version__"]
@@ -0,0 +1,3 @@
1
+ """Package version."""
2
+
3
+ __version__ = "0.2.0" # x-release-please-version
@@ -0,0 +1,64 @@
1
+ """Loopback WebSocket bridge shared by the MCP server and Unity package."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import os
7
+ import threading
8
+ import time
9
+ from typing import Any
10
+
11
+ from dcc_mcp_core.bridge import DccBridge
12
+
13
+ _bridge: DccBridge | None = None
14
+ _lock = threading.Lock()
15
+ _HOST_REQUEST_LIFETIME_SECONDS = 55
16
+
17
+
18
+ def _bridge_timeout() -> float:
19
+ value = float(os.environ.get("DCC_MCP_UNITY_BRIDGE_TIMEOUT", "60"))
20
+ if not math.isfinite(value) or value < 60:
21
+ raise ValueError(
22
+ "DCC_MCP_UNITY_BRIDGE_TIMEOUT must be a finite value of at least 60 seconds"
23
+ )
24
+ return value
25
+
26
+
27
+ def get_bridge() -> DccBridge:
28
+ """Return the process-wide bridge, creating it without starting it."""
29
+ global _bridge
30
+ with _lock:
31
+ if _bridge is None:
32
+ port = int(os.environ.get("DCC_MCP_UNITY_BRIDGE_PORT", "3852"))
33
+ _bridge = DccBridge(
34
+ host="127.0.0.1",
35
+ port=port,
36
+ timeout=_bridge_timeout(),
37
+ server_name="dcc-mcp-unity",
38
+ )
39
+ os.environ.setdefault("DCC_MCP_UNITY_BRIDGE_URL", f"ws://127.0.0.1:{port}")
40
+ return _bridge
41
+
42
+
43
+ def start_bridge() -> DccBridge:
44
+ bridge = get_bridge()
45
+ bridge.connect(wait_for_dcc=False)
46
+ return bridge
47
+
48
+
49
+ def stop_bridge() -> None:
50
+ global _bridge
51
+ with _lock:
52
+ bridge, _bridge = _bridge, None
53
+ if bridge is not None:
54
+ bridge.disconnect()
55
+
56
+
57
+ def call_host(method: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
58
+ """Invoke one typed command in the connected Unity Editor."""
59
+ request_params = dict(params or {})
60
+ request_params["_dcc_mcp_deadline_unix_ms"] = int(
61
+ (time.time() + _HOST_REQUEST_LIFETIME_SECONDS) * 1000
62
+ )
63
+ result = get_bridge().call(method, **request_params)
64
+ return result if isinstance(result, dict) else {"value": result}
@@ -0,0 +1,21 @@
1
+ """In-process skill dispatcher for the external Unity Editor bridge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Callable
6
+
7
+
8
+ class UnityBridgeDispatcher:
9
+ """Run Python wrappers inline; Unity performs host work on its editor update loop."""
10
+
11
+ def dispatch_callable(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
12
+ for key in (
13
+ "affinity",
14
+ "context",
15
+ "action_name",
16
+ "skill_name",
17
+ "execution",
18
+ "timeout_hint_secs",
19
+ ):
20
+ kwargs.pop(key, None)
21
+ return func(*args, **kwargs)
@@ -0,0 +1,67 @@
1
+ """Install the bundled UPM package into a Unity project."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import re
7
+ import shutil
8
+ from pathlib import Path
9
+
10
+ PACKAGE_NAME = "com.dcc-mcp.unity"
11
+ MIN_UNITY_VERSION = (2021, 3)
12
+ _EDITOR_VERSION_PATTERN = re.compile(r"^m_EditorVersion:\s*(\S+)\s*$", re.MULTILINE)
13
+
14
+
15
+ def read_unity_version(project: Path) -> str:
16
+ """Return the exact Editor version recorded by a Unity project."""
17
+ version_file = project / "ProjectSettings" / "ProjectVersion.txt"
18
+ try:
19
+ contents = version_file.read_text(encoding="utf-8-sig")
20
+ except OSError as exc:
21
+ raise ValueError(f"Unity project version file is unavailable: {version_file}") from exc
22
+
23
+ match = _EDITOR_VERSION_PATTERN.search(contents)
24
+ if match is None:
25
+ raise ValueError(f"Unity project version is missing from: {version_file}")
26
+ return match.group(1)
27
+
28
+
29
+ def _require_supported_unity_version(version: str) -> None:
30
+ match = re.match(r"^(\d+)\.(\d+)", version)
31
+ if match is None:
32
+ raise ValueError(f"unsupported Unity version format: {version}")
33
+ parsed = (int(match.group(1)), int(match.group(2)))
34
+ if parsed < MIN_UNITY_VERSION:
35
+ raise ValueError(
36
+ f"Unity {version} is unsupported; DCC-MCP Unity requires Unity 2021.3 or newer"
37
+ )
38
+
39
+
40
+ def install_package(project: Path, *, overwrite: bool = False) -> Path:
41
+ """Copy the bundled package below the target project's Packages directory."""
42
+ project = project.resolve()
43
+ if not (project / "Assets").is_dir() or not (project / "ProjectSettings").is_dir():
44
+ raise ValueError(f"not a Unity project: {project}")
45
+ _require_supported_unity_version(read_unity_version(project))
46
+
47
+ source = Path(__file__).resolve().parent / "unity_package"
48
+ target = project / "Packages" / PACKAGE_NAME
49
+ if target.exists():
50
+ if not overwrite:
51
+ raise FileExistsError(f"package already exists: {target}")
52
+ shutil.rmtree(target)
53
+ target.parent.mkdir(parents=True, exist_ok=True)
54
+ shutil.copytree(source, target)
55
+ return target
56
+
57
+
58
+ def main() -> None:
59
+ parser = argparse.ArgumentParser(description="Install DCC-MCP Unity into a Unity project.")
60
+ parser.add_argument("project", type=Path)
61
+ parser.add_argument("--overwrite", action="store_true")
62
+ args = parser.parse_args()
63
+ print(install_package(args.project, overwrite=args.overwrite))
64
+
65
+
66
+ if __name__ == "__main__":
67
+ main()
@@ -0,0 +1,101 @@
1
+ """Unity MCP server composition and lifecycle."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import signal
7
+ import threading
8
+ from pathlib import Path
9
+ from typing import Any, Optional
10
+
11
+ from dcc_mcp_core import DccServerOptions, HostExecutionBridge
12
+ from dcc_mcp_core.host import QueueDispatcher, StandaloneHost
13
+ from dcc_mcp_core.server_base import DccServerBase
14
+
15
+ from .__version__ import __version__
16
+ from .bridge import start_bridge, stop_bridge
17
+ from .dispatcher import UnityBridgeDispatcher
18
+
19
+ _server: Optional["UnityMcpServer"] = None
20
+
21
+
22
+ class UnityMcpServer(DccServerBase):
23
+ """DCC-MCP server backed by the bundled Unity Editor package."""
24
+
25
+ def __init__(self, port: Optional[int] = None) -> None:
26
+ self._host_dispatcher = QueueDispatcher()
27
+ self._host_driver = StandaloneHost(
28
+ self._host_dispatcher,
29
+ thread_name="dcc-mcp-unity-host",
30
+ )
31
+ execution_bridge = HostExecutionBridge(
32
+ dispatcher=UnityBridgeDispatcher(),
33
+ host_dispatcher=self._host_dispatcher,
34
+ default_thread_affinity="main",
35
+ default_execution="sync",
36
+ default_timeout_hint_secs=60,
37
+ )
38
+ options = DccServerOptions.from_env(
39
+ "unity",
40
+ Path(__file__).resolve().parent / "skills",
41
+ port=port,
42
+ server_name="dcc-mcp-unity",
43
+ server_version=__version__,
44
+ execution_bridge=execution_bridge,
45
+ )
46
+ super().__init__(options=options)
47
+
48
+ def start(self, **kwargs: Any) -> Any:
49
+ start_bridge()
50
+ try:
51
+ self._host_driver.start()
52
+ return super().start(**kwargs)
53
+ except Exception:
54
+ self._host_driver.stop()
55
+ stop_bridge()
56
+ raise
57
+
58
+ def stop(self) -> None:
59
+ try:
60
+ super().stop()
61
+ finally:
62
+ try:
63
+ self._host_driver.stop()
64
+ finally:
65
+ stop_bridge()
66
+
67
+ def _version_string(self) -> str:
68
+ return os.environ.get("DCC_MCP_UNITY_VERSION", "unknown")
69
+
70
+
71
+ def start_server(port: Optional[int] = None) -> UnityMcpServer:
72
+ global _server
73
+ if _server is None or not _server.is_running:
74
+ _server = UnityMcpServer(port)
75
+ _server.register_builtin_actions()
76
+ _server.start()
77
+ return _server
78
+
79
+
80
+ def stop_server() -> None:
81
+ global _server
82
+ if _server is not None:
83
+ _server.stop()
84
+ _server = None
85
+
86
+
87
+ def main() -> None:
88
+ """Run the standalone adapter until interrupted."""
89
+ stopped = threading.Event()
90
+ signal.signal(signal.SIGINT, lambda *_: stopped.set())
91
+ if hasattr(signal, "SIGTERM"):
92
+ signal.signal(signal.SIGTERM, lambda *_: stopped.set())
93
+ start_server()
94
+ try:
95
+ stopped.wait()
96
+ finally:
97
+ stop_server()
98
+
99
+
100
+ if __name__ == "__main__":
101
+ main()
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: unity-diagnostics
3
+ description: >-
4
+ Domain skill — Read a bounded snapshot of Unity Console messages captured
5
+ after the Editor bridge loads. Use to verify operations and diagnose compile,
6
+ import, or runtime errors. Not for clearing logs or arbitrary Editor access.
7
+ license: MIT
8
+ compatibility: "Unity 2021.3+; dcc-mcp-core 0.19.45+"
9
+ allowed-tools: "python"
10
+ metadata:
11
+ dcc-mcp:
12
+ dcc: unity
13
+ layer: domain
14
+ version: "0.2.0" # x-release-please-version
15
+ search-hint: "Unity Console logs errors warnings stack trace diagnostics"
16
+ tags: "unity,console,logs,diagnostics,game-development"
17
+ tools: tools.yaml
18
+ depends: "dcc-diagnostics"
19
+ ---
20
+
21
+ # Unity Diagnostics
22
+
23
+ Read the captured Unity Console after an operation or when the Editor reports a failure. Results
24
+ are bounded and may be truncated; increase `limit` up to 200 or narrow by severity when needed.
@@ -0,0 +1,3 @@
1
+ # Dependencies
2
+
3
+ - dcc-diagnostics
@@ -0,0 +1,16 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+
3
+ from dcc_mcp_unity.bridge import call_host
4
+
5
+
6
+ @skill_entry
7
+ def main(limit: int = 100, severity: str = "all", **_kwargs):
8
+ result = call_host("editor.read_console", {"limit": limit, "severity": severity})
9
+ count = len(result.get("entries", []))
10
+ return skill_success(f"Read {count} captured Unity Console entries.", **result)
11
+
12
+
13
+ if __name__ == "__main__":
14
+ from dcc_mcp_core.skill import run_main
15
+
16
+ run_main(main)
@@ -0,0 +1,21 @@
1
+ tools:
2
+ - name: read_console
3
+ description: Return a bounded, chronological snapshot of Unity Console messages captured since the Editor package loaded. Filter by severity and use after operations or failures. Not for historical logs from before package initialization.
4
+ input_schema:
5
+ type: object
6
+ properties:
7
+ limit: {type: integer, minimum: 1, maximum: 200, default: 100}
8
+ severity: {type: string, enum: [all, error, warning, log], default: all}
9
+ additionalProperties: false
10
+ output_schema: {type: object}
11
+ read_only: true
12
+ destructive: false
13
+ idempotent: true
14
+ execution: sync
15
+ affinity: main
16
+ enforce_thread_affinity: true
17
+ timeout_hint_secs: 60
18
+ source_file: scripts/read_console.py
19
+ next-tools:
20
+ on-success: [unity_project__inspect_project, unity_scene__inspect_scene]
21
+ on-failure: [dcc_diagnostics__screenshot, dcc_diagnostics__audit_log]
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: unity-project
3
+ description: >-
4
+ Domain skill — Inspect the open Unity project and refresh its Asset Database.
5
+ Use for project identity, Unity version, Editor state, active scene, build
6
+ scenes, and asset import refresh. Not for GameObject edits — use unity-scene.
7
+ license: MIT
8
+ compatibility: "Unity 2021.3+; dcc-mcp-core 0.19.45+"
9
+ allowed-tools: "python"
10
+ metadata:
11
+ dcc-mcp:
12
+ dcc: unity
13
+ layer: domain
14
+ version: "0.2.0" # x-release-please-version
15
+ search-hint: "Unity project version build scenes Asset Database refresh"
16
+ tags: "unity,project,assets,game-development"
17
+ tools: tools.yaml
18
+ depends: "dcc-diagnostics"
19
+ ---
20
+
21
+ # Unity Project
22
+
23
+ Inspect before assuming the active project, scene, Unity version, or Play/compile state. Asset
24
+ refresh is an explicit mutation because it may import changed files and update generated metadata.
25
+
26
+ Stop if the returned project path is not the intended target. Do not automatically retry a timed-out
27
+ refresh; inspect the project again first.
@@ -0,0 +1,3 @@
1
+ # Dependencies
2
+
3
+ - dcc-diagnostics
@@ -0,0 +1,14 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+
3
+ from dcc_mcp_unity.bridge import call_host
4
+
5
+
6
+ @skill_entry
7
+ def main(**_kwargs):
8
+ return skill_success("Unity project inspected.", **call_host("project.inspect"))
9
+
10
+
11
+ if __name__ == "__main__":
12
+ from dcc_mcp_core.skill import run_main
13
+
14
+ run_main(main)
@@ -0,0 +1,14 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+
3
+ from dcc_mcp_unity.bridge import call_host
4
+
5
+
6
+ @skill_entry
7
+ def main(**_kwargs):
8
+ return skill_success("Unity assets refreshed.", **call_host("assets.refresh"))
9
+
10
+
11
+ if __name__ == "__main__":
12
+ from dcc_mcp_core.skill import run_main
13
+
14
+ run_main(main)
@@ -0,0 +1,31 @@
1
+ tools:
2
+ - name: inspect_project
3
+ description: Return Unity project identity, editor version and readiness state, active scene, and enabled build scenes. Use before project-sensitive work and stop mutations while Unity reports compiling, updating, or playing. Not for hierarchy inspection; use inspect_scene.
4
+ input_schema: {type: object, properties: {}, additionalProperties: false}
5
+ output_schema: {type: object}
6
+ read_only: true
7
+ destructive: false
8
+ idempotent: true
9
+ execution: sync
10
+ affinity: main
11
+ enforce_thread_affinity: true
12
+ timeout_hint_secs: 60
13
+ source_file: scripts/inspect_project.py
14
+ next-tools:
15
+ on-success: [unity_scene__inspect_scene, unity_project__refresh_assets]
16
+ on-failure: [dcc_diagnostics__screenshot, dcc_diagnostics__audit_log]
17
+ - name: refresh_assets
18
+ description: Ask Unity AssetDatabase to discover and import changed project assets. Use after external file changes. Not for saving scene edits; use save_scene.
19
+ input_schema: {type: object, properties: {}, additionalProperties: false}
20
+ output_schema: {type: object}
21
+ read_only: false
22
+ destructive: false
23
+ idempotent: true
24
+ execution: sync
25
+ affinity: main
26
+ enforce_thread_affinity: true
27
+ timeout_hint_secs: 60
28
+ source_file: scripts/refresh_assets.py
29
+ next-tools:
30
+ on-success: [unity_project__inspect_project]
31
+ on-failure: [dcc_diagnostics__screenshot, dcc_diagnostics__audit_log]
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: unity-scene
3
+ description: >-
4
+ Domain skill — Inspect and edit the active Unity scene with typed, undoable
5
+ GameObject operations. Use for hierarchy, object creation, transforms, and
6
+ scene saves. Not for asset imports — use unity-project.
7
+ license: MIT
8
+ compatibility: "Unity 2021.3+; dcc-mcp-core 0.19.45+"
9
+ allowed-tools: "python"
10
+ metadata:
11
+ dcc-mcp:
12
+ dcc: unity
13
+ layer: domain
14
+ version: "0.2.0" # x-release-please-version
15
+ search-hint: "Unity scene hierarchy GameObject transform save Undo"
16
+ tags: "unity,scene,gameobject,transform,game-development"
17
+ tools: tools.yaml
18
+ depends: "dcc-diagnostics"
19
+ ---
20
+
21
+ # Unity Scene
22
+
23
+ Inspect the hierarchy immediately before using instance IDs; Unity instance IDs are scoped to the
24
+ current Editor session. Object creation and transform edits register with Unity Undo.
25
+
26
+ Do not automatically retry a timed-out mutation. Inspect the project and scene first because the
27
+ Editor may have completed the original request near the timeout boundary.
@@ -0,0 +1,3 @@
1
+ # Dependencies
2
+
3
+ - dcc-diagnostics
@@ -0,0 +1,18 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+
3
+ from dcc_mcp_unity.bridge import call_host
4
+
5
+
6
+ @skill_entry
7
+ def main(name: str, parent_instance_id: int = 0, **_kwargs):
8
+ result = call_host(
9
+ "scene.create_game_object",
10
+ {"name": name, "parent_instance_id": parent_instance_id},
11
+ )
12
+ return skill_success(f"Created Unity GameObject {name}.", **result)
13
+
14
+
15
+ if __name__ == "__main__":
16
+ from dcc_mcp_core.skill import run_main
17
+
18
+ run_main(main)
@@ -0,0 +1,15 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+
3
+ from dcc_mcp_unity.bridge import call_host
4
+
5
+
6
+ @skill_entry
7
+ def main(max_nodes: int = 1000, **_kwargs):
8
+ result = call_host("scene.inspect", {"max_nodes": max_nodes})
9
+ return skill_success("Unity scene inspected.", **result)
10
+
11
+
12
+ if __name__ == "__main__":
13
+ from dcc_mcp_core.skill import run_main
14
+
15
+ run_main(main)
@@ -0,0 +1,14 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+
3
+ from dcc_mcp_unity.bridge import call_host
4
+
5
+
6
+ @skill_entry
7
+ def main(**_kwargs):
8
+ return skill_success("Unity scenes saved.", **call_host("scene.save"))
9
+
10
+
11
+ if __name__ == "__main__":
12
+ from dcc_mcp_core.skill import run_main
13
+
14
+ run_main(main)
@@ -0,0 +1,31 @@
1
+ from typing import List, Optional
2
+
3
+ from dcc_mcp_core.skill import skill_entry, skill_success
4
+
5
+ from dcc_mcp_unity.bridge import call_host
6
+
7
+
8
+ @skill_entry
9
+ def main(
10
+ instance_id: int,
11
+ position: Optional[List[float]] = None,
12
+ rotation_euler: Optional[List[float]] = None,
13
+ scale: Optional[List[float]] = None,
14
+ **_kwargs,
15
+ ):
16
+ params = {"instance_id": instance_id}
17
+ for name, value in (
18
+ ("position", position),
19
+ ("rotation_euler", rotation_euler),
20
+ ("scale", scale),
21
+ ):
22
+ if value is not None:
23
+ params[name] = value
24
+ result = call_host("scene.set_transform", params)
25
+ return skill_success(f"Updated Unity transform {instance_id}.", **result)
26
+
27
+
28
+ if __name__ == "__main__":
29
+ from dcc_mcp_core.skill import run_main
30
+
31
+ run_main(main)
@@ -0,0 +1,95 @@
1
+ tools:
2
+ - name: inspect_scene
3
+ description: Return a bounded active Unity scene hierarchy up to eight levels deep with session-scoped GameObject instance IDs. Use immediately before choosing targets and lower max_nodes for smaller responses. Not for project metadata; use inspect_project.
4
+ input_schema:
5
+ type: object
6
+ properties:
7
+ max_nodes: {type: integer, minimum: 1, maximum: 5000, default: 1000}
8
+ additionalProperties: false
9
+ output_schema: {type: object}
10
+ read_only: true
11
+ destructive: false
12
+ idempotent: true
13
+ execution: sync
14
+ affinity: main
15
+ enforce_thread_affinity: true
16
+ timeout_hint_secs: 60
17
+ source_file: scripts/inspect_scene.py
18
+ next-tools:
19
+ on-success: [unity_scene__create_game_object, unity_scene__set_transform]
20
+ on-failure: [dcc_diagnostics__screenshot, dcc_diagnostics__audit_log]
21
+ - name: create_game_object
22
+ description: Create one undoable GameObject in the active Unity scene. Use a parent instance ID from a fresh inspect_scene result, or omit it for a scene root. Not for prefab or asset creation.
23
+ input_schema:
24
+ type: object
25
+ required: [name]
26
+ properties:
27
+ name: {type: string, minLength: 1, maxLength: 120}
28
+ parent_instance_id: {type: integer, default: 0}
29
+ additionalProperties: false
30
+ output_schema: {type: object}
31
+ read_only: false
32
+ destructive: false
33
+ idempotent: false
34
+ execution: sync
35
+ affinity: main
36
+ enforce_thread_affinity: true
37
+ timeout_hint_secs: 60
38
+ source_file: scripts/create_game_object.py
39
+ next-tools:
40
+ on-success: [unity_scene__set_transform, unity_scene__save_scene]
41
+ on-failure: [unity_scene__inspect_scene, dcc_diagnostics__audit_log]
42
+ - name: set_transform
43
+ description: Set selected world position, world Euler rotation, or local scale on a Unity GameObject through Undo. Use a session-scoped instance ID from a fresh inspect_scene. Not for RectTransform layout properties.
44
+ input_schema:
45
+ type: object
46
+ required: [instance_id]
47
+ properties:
48
+ instance_id: {type: integer}
49
+ position:
50
+ type: array
51
+ items: {type: number}
52
+ minItems: 3
53
+ maxItems: 3
54
+ rotation_euler:
55
+ type: array
56
+ items: {type: number}
57
+ minItems: 3
58
+ maxItems: 3
59
+ scale:
60
+ type: array
61
+ items: {type: number}
62
+ minItems: 3
63
+ maxItems: 3
64
+ anyOf:
65
+ - required: [position]
66
+ - required: [rotation_euler]
67
+ - required: [scale]
68
+ additionalProperties: false
69
+ output_schema: {type: object}
70
+ read_only: false
71
+ destructive: true
72
+ idempotent: true
73
+ execution: sync
74
+ affinity: main
75
+ enforce_thread_affinity: true
76
+ timeout_hint_secs: 60
77
+ source_file: scripts/set_transform.py
78
+ next-tools:
79
+ on-success: [unity_scene__save_scene, unity_scene__inspect_scene]
80
+ on-failure: [unity_scene__inspect_scene, dcc_diagnostics__audit_log]
81
+ - name: save_scene
82
+ description: Save all currently open Unity scenes. Use after verified scene mutations. Not for importing assets; use refresh_assets.
83
+ input_schema: {type: object, properties: {}, additionalProperties: false}
84
+ output_schema: {type: object}
85
+ read_only: false
86
+ destructive: true
87
+ idempotent: true
88
+ execution: sync
89
+ affinity: main
90
+ enforce_thread_affinity: true
91
+ timeout_hint_secs: 60
92
+ source_file: scripts/save_scene.py
93
+ next-tools:
94
+ on-success: [unity_scene__inspect_scene]
95
+ on-failure: [dcc_diagnostics__screenshot, dcc_diagnostics__audit_log]
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "DccMcp.Unity.Editor",
3
+ "rootNamespace": "DccMcp.Unity",
4
+ "references": [],
5
+ "includePlatforms": [
6
+ "Editor"
7
+ ],
8
+ "autoReferenced": true
9
+ }