mcpforunityserver 8.2.3__tar.gz → 8.5.0__tar.gz
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.
- {mcpforunityserver-8.2.3/src/MCPForUnityServer.egg-info → mcpforunityserver-8.5.0}/PKG-INFO +38 -66
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/README.md +35 -63
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/pyproject.toml +4 -4
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/main.py +38 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0/src/mcpforunityserver.egg-info}/PKG-INFO +38 -66
- {mcpforunityserver-8.2.3/src/MCPForUnityServer.egg-info → mcpforunityserver-8.5.0/src/mcpforunityserver.egg-info}/SOURCES.txt +1 -6
- {mcpforunityserver-8.2.3/src/MCPForUnityServer.egg-info → mcpforunityserver-8.5.0/src/mcpforunityserver.egg-info}/requires.txt +2 -2
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/debug_request_context.py +9 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/manage_asset.py +38 -24
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/manage_gameobject.py +15 -1
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/manage_scene.py +33 -18
- mcpforunityserver-8.5.0/src/services/tools/manage_scriptable_object.py +75 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/read_console.py +3 -30
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/run_tests.py +2 -17
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/utils.py +17 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/LICENSE +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/setup.cfg +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/core/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/core/config.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/core/logging_decorator.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/core/telemetry.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/core/telemetry_decorator.py +0 -0
- {mcpforunityserver-8.2.3/src/MCPForUnityServer.egg-info → mcpforunityserver-8.5.0/src/mcpforunityserver.egg-info}/dependency_links.txt +0 -0
- {mcpforunityserver-8.2.3/src/MCPForUnityServer.egg-info → mcpforunityserver-8.5.0/src/mcpforunityserver.egg-info}/entry_points.txt +0 -0
- {mcpforunityserver-8.2.3/src/MCPForUnityServer.egg-info → mcpforunityserver-8.5.0/src/mcpforunityserver.egg-info}/top_level.txt +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/models/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/models/models.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/models/unity_response.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/routes/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/custom_tool_service.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/registry/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/registry/resource_registry.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/registry/tool_registry.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/active_tool.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/custom_tools.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/editor_state.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/layers.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/menu_items.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/prefab_stage.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/project_info.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/selection.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/tags.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/tests.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/unity_instances.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/windows.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/batch_execute.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/execute_custom_tool.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/execute_menu_item.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/find_in_file.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/manage_editor.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/manage_material.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/manage_prefabs.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/manage_script.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/manage_shader.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/script_apply_edits.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/set_active_instance.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/__init__.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/legacy/port_discovery.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/legacy/stdio_port_registry.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/legacy/unity_connection.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/models.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/plugin_hub.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/plugin_registry.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/unity_instance_middleware.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/unity_transport.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/utils/module_discovery.py +0 -0
- {mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/utils/reload_sentinel.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpforunityserver
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.5.0
|
|
4
4
|
Summary: MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP).
|
|
5
5
|
Author-email: Marcus Sanatan <msanatan@gmail.com>, David Sarno <david.sarno@gmail.com>, Wu Shutong <martinwfire@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -26,9 +26,9 @@ Requires-Python: >=3.10
|
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
License-File: LICENSE
|
|
28
28
|
Requires-Dist: httpx>=0.27.2
|
|
29
|
-
Requires-Dist: fastmcp
|
|
29
|
+
Requires-Dist: fastmcp==2.14.1
|
|
30
30
|
Requires-Dist: mcp>=1.16.0
|
|
31
|
-
Requires-Dist: pydantic>=2.12.
|
|
31
|
+
Requires-Dist: pydantic>=2.12.5
|
|
32
32
|
Requires-Dist: tomli>=2.3.0
|
|
33
33
|
Requires-Dist: fastapi>=0.104.0
|
|
34
34
|
Requires-Dist: uvicorn>=0.35.0
|
|
@@ -50,24 +50,20 @@ Model Context Protocol server for Unity Editor integration. Control Unity throug
|
|
|
50
50
|
|
|
51
51
|
💬 **Join our community:** [Discord Server](https://discord.gg/y4p8KfzrN4)
|
|
52
52
|
|
|
53
|
-
**Required:** Install the [Unity MCP Plugin](https://github.com/CoplayDev/unity-mcp?tab=readme-ov-file#-step-1-install-the-unity-package) to connect Unity Editor with this MCP server.
|
|
53
|
+
**Required:** Install the [Unity MCP Plugin](https://github.com/CoplayDev/unity-mcp?tab=readme-ov-file#-step-1-install-the-unity-package) to connect Unity Editor with this MCP server. You also need `uvx` (requires [uv](https://docs.astral.sh/uv/)) to run the server.
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
57
|
## Installation
|
|
58
58
|
|
|
59
|
-
### Option 1:
|
|
59
|
+
### Option 1: PyPI
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Install and run directly from PyPI using `uvx`.
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
# HTTP (default)
|
|
65
|
-
uvx --from git+https://github.com/CoplayDev/unity-mcp@v8.2.3#subdirectory=Server \
|
|
66
|
-
mcp-for-unity --transport http --http-url http://localhost:8080
|
|
63
|
+
**Run Server (HTTP):**
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
uvx --from
|
|
70
|
-
mcp-for-unity --transport stdio
|
|
65
|
+
```bash
|
|
66
|
+
uvx --from mcpforunityserver mcp-for-unity --transport http --http-url http://localhost:8080
|
|
71
67
|
```
|
|
72
68
|
|
|
73
69
|
**MCP Client Configuration (HTTP):**
|
|
@@ -91,55 +87,29 @@ uvx --from git+https://github.com/CoplayDev/unity-mcp@v8.2.3#subdirectory=Server
|
|
|
91
87
|
"command": "uvx",
|
|
92
88
|
"args": [
|
|
93
89
|
"--from",
|
|
94
|
-
"
|
|
90
|
+
"mcpforunityserver",
|
|
95
91
|
"mcp-for-unity",
|
|
96
92
|
"--transport",
|
|
97
93
|
"stdio"
|
|
98
|
-
]
|
|
99
|
-
"type": "stdio"
|
|
94
|
+
]
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
97
|
}
|
|
103
98
|
```
|
|
104
99
|
|
|
105
|
-
### Option 2:
|
|
106
|
-
|
|
107
|
-
For local development or custom installations:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
# Clone the repository
|
|
111
|
-
git clone https://github.com/CoplayDev/unity-mcp.git
|
|
112
|
-
cd unity-mcp/Server
|
|
113
|
-
|
|
114
|
-
# Run with uv (HTTP)
|
|
115
|
-
uv run server.py --transport http --http-url http://localhost:8080
|
|
116
|
-
|
|
117
|
-
# Run with uv (stdio)
|
|
118
|
-
uv run server.py --transport stdio
|
|
119
|
-
```
|
|
100
|
+
### Option 2: From GitHub Source
|
|
120
101
|
|
|
121
|
-
|
|
122
|
-
```json
|
|
123
|
-
{
|
|
124
|
-
"mcpServers": {
|
|
125
|
-
"UnityMCP": {
|
|
126
|
-
"url": "http://localhost:8080/mcp"
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
102
|
+
Use this to run the latest released version from the repository. Change the version to `main` to run the latest unreleased changes from the repository.
|
|
131
103
|
|
|
132
|
-
**MCP Client Configuration (stdio – Windows):**
|
|
133
104
|
```json
|
|
134
105
|
{
|
|
135
106
|
"mcpServers": {
|
|
136
107
|
"UnityMCP": {
|
|
137
|
-
"command": "
|
|
108
|
+
"command": "uvx",
|
|
138
109
|
"args": [
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"server.py",
|
|
110
|
+
"--from",
|
|
111
|
+
"git+https://github.com/CoplayDev/unity-mcp@v8.5.0#subdirectory=Server",
|
|
112
|
+
"mcp-for-unity",
|
|
143
113
|
"--transport",
|
|
144
114
|
"stdio"
|
|
145
115
|
]
|
|
@@ -148,33 +118,35 @@ uv run server.py --transport stdio
|
|
|
148
118
|
}
|
|
149
119
|
```
|
|
150
120
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"args": [
|
|
158
|
-
"run",
|
|
159
|
-
"--directory",
|
|
160
|
-
"/path/to/unity-mcp/Server",
|
|
161
|
-
"server.py",
|
|
162
|
-
"--transport",
|
|
163
|
-
"stdio"
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
121
|
+
### Option 3: Docker
|
|
122
|
+
|
|
123
|
+
**Use Pre-built Image:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
docker run -p 8080:8080 msanatan/mcp-for-unity-server:latest --transport http --http-url http://0.0.0.0:8080
|
|
168
127
|
```
|
|
169
128
|
|
|
170
|
-
|
|
129
|
+
**Build Locally:**
|
|
171
130
|
|
|
172
131
|
```bash
|
|
173
132
|
docker build -t unity-mcp-server .
|
|
174
133
|
docker run -p 8080:8080 unity-mcp-server --transport http --http-url http://0.0.0.0:8080
|
|
175
134
|
```
|
|
176
135
|
|
|
177
|
-
Configure your MCP client with `"url": "http://localhost:8080/mcp"`.
|
|
136
|
+
Configure your MCP client with `"url": "http://localhost:8080/mcp"`.
|
|
137
|
+
|
|
138
|
+
### Option 4: Local Development
|
|
139
|
+
|
|
140
|
+
For contributing or modifying the server code:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Clone the repository
|
|
144
|
+
git clone https://github.com/CoplayDev/unity-mcp.git
|
|
145
|
+
cd unity-mcp/Server
|
|
146
|
+
|
|
147
|
+
# Run with uv
|
|
148
|
+
uv run src/main.py --transport stdio
|
|
149
|
+
```
|
|
178
150
|
|
|
179
151
|
---
|
|
180
152
|
|
|
@@ -11,24 +11,20 @@ Model Context Protocol server for Unity Editor integration. Control Unity throug
|
|
|
11
11
|
|
|
12
12
|
💬 **Join our community:** [Discord Server](https://discord.gg/y4p8KfzrN4)
|
|
13
13
|
|
|
14
|
-
**Required:** Install the [Unity MCP Plugin](https://github.com/CoplayDev/unity-mcp?tab=readme-ov-file#-step-1-install-the-unity-package) to connect Unity Editor with this MCP server.
|
|
14
|
+
**Required:** Install the [Unity MCP Plugin](https://github.com/CoplayDev/unity-mcp?tab=readme-ov-file#-step-1-install-the-unity-package) to connect Unity Editor with this MCP server. You also need `uvx` (requires [uv](https://docs.astral.sh/uv/)) to run the server.
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
20
|
-
### Option 1:
|
|
20
|
+
### Option 1: PyPI
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Install and run directly from PyPI using `uvx`.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
# HTTP (default)
|
|
26
|
-
uvx --from git+https://github.com/CoplayDev/unity-mcp@v8.2.3#subdirectory=Server \
|
|
27
|
-
mcp-for-unity --transport http --http-url http://localhost:8080
|
|
24
|
+
**Run Server (HTTP):**
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
uvx --from
|
|
31
|
-
mcp-for-unity --transport stdio
|
|
26
|
+
```bash
|
|
27
|
+
uvx --from mcpforunityserver mcp-for-unity --transport http --http-url http://localhost:8080
|
|
32
28
|
```
|
|
33
29
|
|
|
34
30
|
**MCP Client Configuration (HTTP):**
|
|
@@ -52,55 +48,29 @@ uvx --from git+https://github.com/CoplayDev/unity-mcp@v8.2.3#subdirectory=Server
|
|
|
52
48
|
"command": "uvx",
|
|
53
49
|
"args": [
|
|
54
50
|
"--from",
|
|
55
|
-
"
|
|
51
|
+
"mcpforunityserver",
|
|
56
52
|
"mcp-for-unity",
|
|
57
53
|
"--transport",
|
|
58
54
|
"stdio"
|
|
59
|
-
]
|
|
60
|
-
"type": "stdio"
|
|
55
|
+
]
|
|
61
56
|
}
|
|
62
57
|
}
|
|
63
58
|
}
|
|
64
59
|
```
|
|
65
60
|
|
|
66
|
-
### Option 2:
|
|
67
|
-
|
|
68
|
-
For local development or custom installations:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
# Clone the repository
|
|
72
|
-
git clone https://github.com/CoplayDev/unity-mcp.git
|
|
73
|
-
cd unity-mcp/Server
|
|
74
|
-
|
|
75
|
-
# Run with uv (HTTP)
|
|
76
|
-
uv run server.py --transport http --http-url http://localhost:8080
|
|
77
|
-
|
|
78
|
-
# Run with uv (stdio)
|
|
79
|
-
uv run server.py --transport stdio
|
|
80
|
-
```
|
|
61
|
+
### Option 2: From GitHub Source
|
|
81
62
|
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"mcpServers": {
|
|
86
|
-
"UnityMCP": {
|
|
87
|
-
"url": "http://localhost:8080/mcp"
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
```
|
|
63
|
+
Use this to run the latest released version from the repository. Change the version to `main` to run the latest unreleased changes from the repository.
|
|
92
64
|
|
|
93
|
-
**MCP Client Configuration (stdio – Windows):**
|
|
94
65
|
```json
|
|
95
66
|
{
|
|
96
67
|
"mcpServers": {
|
|
97
68
|
"UnityMCP": {
|
|
98
|
-
"command": "
|
|
69
|
+
"command": "uvx",
|
|
99
70
|
"args": [
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"server.py",
|
|
71
|
+
"--from",
|
|
72
|
+
"git+https://github.com/CoplayDev/unity-mcp@v8.5.0#subdirectory=Server",
|
|
73
|
+
"mcp-for-unity",
|
|
104
74
|
"--transport",
|
|
105
75
|
"stdio"
|
|
106
76
|
]
|
|
@@ -109,33 +79,35 @@ uv run server.py --transport stdio
|
|
|
109
79
|
}
|
|
110
80
|
```
|
|
111
81
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
"args": [
|
|
119
|
-
"run",
|
|
120
|
-
"--directory",
|
|
121
|
-
"/path/to/unity-mcp/Server",
|
|
122
|
-
"server.py",
|
|
123
|
-
"--transport",
|
|
124
|
-
"stdio"
|
|
125
|
-
]
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
82
|
+
### Option 3: Docker
|
|
83
|
+
|
|
84
|
+
**Use Pre-built Image:**
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
docker run -p 8080:8080 msanatan/mcp-for-unity-server:latest --transport http --http-url http://0.0.0.0:8080
|
|
129
88
|
```
|
|
130
89
|
|
|
131
|
-
|
|
90
|
+
**Build Locally:**
|
|
132
91
|
|
|
133
92
|
```bash
|
|
134
93
|
docker build -t unity-mcp-server .
|
|
135
94
|
docker run -p 8080:8080 unity-mcp-server --transport http --http-url http://0.0.0.0:8080
|
|
136
95
|
```
|
|
137
96
|
|
|
138
|
-
Configure your MCP client with `"url": "http://localhost:8080/mcp"`.
|
|
97
|
+
Configure your MCP client with `"url": "http://localhost:8080/mcp"`.
|
|
98
|
+
|
|
99
|
+
### Option 4: Local Development
|
|
100
|
+
|
|
101
|
+
For contributing or modifying the server code:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Clone the repository
|
|
105
|
+
git clone https://github.com/CoplayDev/unity-mcp.git
|
|
106
|
+
cd unity-mcp/Server
|
|
107
|
+
|
|
108
|
+
# Run with uv
|
|
109
|
+
uv run src/main.py --transport stdio
|
|
110
|
+
```
|
|
139
111
|
|
|
140
112
|
---
|
|
141
113
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mcpforunityserver"
|
|
3
|
-
version = "8.
|
|
3
|
+
version = "8.5.0"
|
|
4
4
|
description = "MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP)."
|
|
5
|
-
license = "MIT"
|
|
6
5
|
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
7
|
authors = [
|
|
8
8
|
{name = "Marcus Sanatan", email = "msanatan@gmail.com"},
|
|
9
9
|
{name = "David Sarno", email = "david.sarno@gmail.com"},
|
|
@@ -30,9 +30,9 @@ keywords = ["mcp", "unity", "ai", "model context protocol", "gamedev", "unity3d"
|
|
|
30
30
|
requires-python = ">=3.10"
|
|
31
31
|
dependencies = [
|
|
32
32
|
"httpx>=0.27.2",
|
|
33
|
-
"fastmcp
|
|
33
|
+
"fastmcp==2.14.1",
|
|
34
34
|
"mcp>=1.16.0",
|
|
35
|
-
"pydantic>=2.12.
|
|
35
|
+
"pydantic>=2.12.5",
|
|
36
36
|
"tomli>=2.3.0",
|
|
37
37
|
"fastapi>=0.104.0",
|
|
38
38
|
"uvicorn>=0.35.0",
|
|
@@ -8,6 +8,32 @@ import time
|
|
|
8
8
|
from typing import AsyncIterator, Any
|
|
9
9
|
from urllib.parse import urlparse
|
|
10
10
|
|
|
11
|
+
# Workaround for environments where tool signature evaluation runs with a globals
|
|
12
|
+
# dict that does not include common `typing` names (e.g. when annotations are strings
|
|
13
|
+
# and evaluated via `eval()` during schema generation).
|
|
14
|
+
# Making these names available in builtins avoids `NameError: Annotated/Literal/... is not defined`.
|
|
15
|
+
try: # pragma: no cover - startup safety guard
|
|
16
|
+
import builtins
|
|
17
|
+
import typing as _typing
|
|
18
|
+
|
|
19
|
+
_typing_names = (
|
|
20
|
+
"Annotated",
|
|
21
|
+
"Literal",
|
|
22
|
+
"Any",
|
|
23
|
+
"Union",
|
|
24
|
+
"Optional",
|
|
25
|
+
"Dict",
|
|
26
|
+
"List",
|
|
27
|
+
"Tuple",
|
|
28
|
+
"Set",
|
|
29
|
+
"FrozenSet",
|
|
30
|
+
)
|
|
31
|
+
for _name in _typing_names:
|
|
32
|
+
if not hasattr(builtins, _name) and hasattr(_typing, _name):
|
|
33
|
+
setattr(builtins, _name, getattr(_typing, _name)) # type: ignore[attr-defined]
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
|
|
11
37
|
from fastmcp import FastMCP
|
|
12
38
|
from logging.handlers import RotatingFileHandler
|
|
13
39
|
from starlette.requests import Request
|
|
@@ -242,6 +268,18 @@ Console Monitoring:
|
|
|
242
268
|
Menu Items:
|
|
243
269
|
- Use `execute_menu_item` when you have read the menu items resource
|
|
244
270
|
- This lets you interact with Unity's menu system and third-party tools
|
|
271
|
+
|
|
272
|
+
Payload sizing & paging (important):
|
|
273
|
+
- Many Unity queries can return very large JSON. Prefer **paged + summary-first** calls.
|
|
274
|
+
- `manage_scene(action="get_hierarchy")`:
|
|
275
|
+
- Use `page_size` + `cursor` and follow `next_cursor` until null.
|
|
276
|
+
- `page_size` is **items per page**; recommended starting point: **50**.
|
|
277
|
+
- `manage_gameobject(action="get_components")`:
|
|
278
|
+
- Start with `include_properties=false` (metadata-only) and small `page_size` (e.g. **10-25**).
|
|
279
|
+
- Only request `include_properties=true` when needed; keep `page_size` small (e.g. **3-10**) to bound payloads.
|
|
280
|
+
- `manage_asset(action="search")`:
|
|
281
|
+
- Use paging (`page_size`, `page_number`) and keep `page_size` modest (e.g. **25-50**) to avoid token-heavy responses.
|
|
282
|
+
- Keep `generate_preview=false` unless you explicitly need thumbnails (previews may include large base64 payloads).
|
|
245
283
|
"""
|
|
246
284
|
)
|
|
247
285
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpforunityserver
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.5.0
|
|
4
4
|
Summary: MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP).
|
|
5
5
|
Author-email: Marcus Sanatan <msanatan@gmail.com>, David Sarno <david.sarno@gmail.com>, Wu Shutong <martinwfire@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -26,9 +26,9 @@ Requires-Python: >=3.10
|
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
License-File: LICENSE
|
|
28
28
|
Requires-Dist: httpx>=0.27.2
|
|
29
|
-
Requires-Dist: fastmcp
|
|
29
|
+
Requires-Dist: fastmcp==2.14.1
|
|
30
30
|
Requires-Dist: mcp>=1.16.0
|
|
31
|
-
Requires-Dist: pydantic>=2.12.
|
|
31
|
+
Requires-Dist: pydantic>=2.12.5
|
|
32
32
|
Requires-Dist: tomli>=2.3.0
|
|
33
33
|
Requires-Dist: fastapi>=0.104.0
|
|
34
34
|
Requires-Dist: uvicorn>=0.35.0
|
|
@@ -50,24 +50,20 @@ Model Context Protocol server for Unity Editor integration. Control Unity throug
|
|
|
50
50
|
|
|
51
51
|
💬 **Join our community:** [Discord Server](https://discord.gg/y4p8KfzrN4)
|
|
52
52
|
|
|
53
|
-
**Required:** Install the [Unity MCP Plugin](https://github.com/CoplayDev/unity-mcp?tab=readme-ov-file#-step-1-install-the-unity-package) to connect Unity Editor with this MCP server.
|
|
53
|
+
**Required:** Install the [Unity MCP Plugin](https://github.com/CoplayDev/unity-mcp?tab=readme-ov-file#-step-1-install-the-unity-package) to connect Unity Editor with this MCP server. You also need `uvx` (requires [uv](https://docs.astral.sh/uv/)) to run the server.
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
57
|
## Installation
|
|
58
58
|
|
|
59
|
-
### Option 1:
|
|
59
|
+
### Option 1: PyPI
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Install and run directly from PyPI using `uvx`.
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
# HTTP (default)
|
|
65
|
-
uvx --from git+https://github.com/CoplayDev/unity-mcp@v8.2.3#subdirectory=Server \
|
|
66
|
-
mcp-for-unity --transport http --http-url http://localhost:8080
|
|
63
|
+
**Run Server (HTTP):**
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
uvx --from
|
|
70
|
-
mcp-for-unity --transport stdio
|
|
65
|
+
```bash
|
|
66
|
+
uvx --from mcpforunityserver mcp-for-unity --transport http --http-url http://localhost:8080
|
|
71
67
|
```
|
|
72
68
|
|
|
73
69
|
**MCP Client Configuration (HTTP):**
|
|
@@ -91,55 +87,29 @@ uvx --from git+https://github.com/CoplayDev/unity-mcp@v8.2.3#subdirectory=Server
|
|
|
91
87
|
"command": "uvx",
|
|
92
88
|
"args": [
|
|
93
89
|
"--from",
|
|
94
|
-
"
|
|
90
|
+
"mcpforunityserver",
|
|
95
91
|
"mcp-for-unity",
|
|
96
92
|
"--transport",
|
|
97
93
|
"stdio"
|
|
98
|
-
]
|
|
99
|
-
"type": "stdio"
|
|
94
|
+
]
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
97
|
}
|
|
103
98
|
```
|
|
104
99
|
|
|
105
|
-
### Option 2:
|
|
106
|
-
|
|
107
|
-
For local development or custom installations:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
# Clone the repository
|
|
111
|
-
git clone https://github.com/CoplayDev/unity-mcp.git
|
|
112
|
-
cd unity-mcp/Server
|
|
113
|
-
|
|
114
|
-
# Run with uv (HTTP)
|
|
115
|
-
uv run server.py --transport http --http-url http://localhost:8080
|
|
116
|
-
|
|
117
|
-
# Run with uv (stdio)
|
|
118
|
-
uv run server.py --transport stdio
|
|
119
|
-
```
|
|
100
|
+
### Option 2: From GitHub Source
|
|
120
101
|
|
|
121
|
-
|
|
122
|
-
```json
|
|
123
|
-
{
|
|
124
|
-
"mcpServers": {
|
|
125
|
-
"UnityMCP": {
|
|
126
|
-
"url": "http://localhost:8080/mcp"
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
102
|
+
Use this to run the latest released version from the repository. Change the version to `main` to run the latest unreleased changes from the repository.
|
|
131
103
|
|
|
132
|
-
**MCP Client Configuration (stdio – Windows):**
|
|
133
104
|
```json
|
|
134
105
|
{
|
|
135
106
|
"mcpServers": {
|
|
136
107
|
"UnityMCP": {
|
|
137
|
-
"command": "
|
|
108
|
+
"command": "uvx",
|
|
138
109
|
"args": [
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"server.py",
|
|
110
|
+
"--from",
|
|
111
|
+
"git+https://github.com/CoplayDev/unity-mcp@v8.5.0#subdirectory=Server",
|
|
112
|
+
"mcp-for-unity",
|
|
143
113
|
"--transport",
|
|
144
114
|
"stdio"
|
|
145
115
|
]
|
|
@@ -148,33 +118,35 @@ uv run server.py --transport stdio
|
|
|
148
118
|
}
|
|
149
119
|
```
|
|
150
120
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"args": [
|
|
158
|
-
"run",
|
|
159
|
-
"--directory",
|
|
160
|
-
"/path/to/unity-mcp/Server",
|
|
161
|
-
"server.py",
|
|
162
|
-
"--transport",
|
|
163
|
-
"stdio"
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
121
|
+
### Option 3: Docker
|
|
122
|
+
|
|
123
|
+
**Use Pre-built Image:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
docker run -p 8080:8080 msanatan/mcp-for-unity-server:latest --transport http --http-url http://0.0.0.0:8080
|
|
168
127
|
```
|
|
169
128
|
|
|
170
|
-
|
|
129
|
+
**Build Locally:**
|
|
171
130
|
|
|
172
131
|
```bash
|
|
173
132
|
docker build -t unity-mcp-server .
|
|
174
133
|
docker run -p 8080:8080 unity-mcp-server --transport http --http-url http://0.0.0.0:8080
|
|
175
134
|
```
|
|
176
135
|
|
|
177
|
-
Configure your MCP client with `"url": "http://localhost:8080/mcp"`.
|
|
136
|
+
Configure your MCP client with `"url": "http://localhost:8080/mcp"`.
|
|
137
|
+
|
|
138
|
+
### Option 4: Local Development
|
|
139
|
+
|
|
140
|
+
For contributing or modifying the server code:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Clone the repository
|
|
144
|
+
git clone https://github.com/CoplayDev/unity-mcp.git
|
|
145
|
+
cd unity-mcp/Server
|
|
146
|
+
|
|
147
|
+
# Run with uv
|
|
148
|
+
uv run src/main.py --transport stdio
|
|
149
|
+
```
|
|
178
150
|
|
|
179
151
|
---
|
|
180
152
|
|
|
@@ -3,12 +3,6 @@ README.md
|
|
|
3
3
|
pyproject.toml
|
|
4
4
|
src/__init__.py
|
|
5
5
|
src/main.py
|
|
6
|
-
src/MCPForUnityServer.egg-info/PKG-INFO
|
|
7
|
-
src/MCPForUnityServer.egg-info/SOURCES.txt
|
|
8
|
-
src/MCPForUnityServer.egg-info/dependency_links.txt
|
|
9
|
-
src/MCPForUnityServer.egg-info/entry_points.txt
|
|
10
|
-
src/MCPForUnityServer.egg-info/requires.txt
|
|
11
|
-
src/MCPForUnityServer.egg-info/top_level.txt
|
|
12
6
|
src/core/__init__.py
|
|
13
7
|
src/core/config.py
|
|
14
8
|
src/core/logging_decorator.py
|
|
@@ -55,6 +49,7 @@ src/services/tools/manage_material.py
|
|
|
55
49
|
src/services/tools/manage_prefabs.py
|
|
56
50
|
src/services/tools/manage_scene.py
|
|
57
51
|
src/services/tools/manage_script.py
|
|
52
|
+
src/services/tools/manage_scriptable_object.py
|
|
58
53
|
src/services/tools/manage_shader.py
|
|
59
54
|
src/services/tools/read_console.py
|
|
60
55
|
src/services/tools/run_tests.py
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/debug_request_context.py
RENAMED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
from typing import Any
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from core.telemetry import get_package_version
|
|
2
6
|
|
|
3
7
|
from fastmcp import Context
|
|
4
8
|
from services.registry import mcp_for_unity_tool
|
|
@@ -50,6 +54,11 @@ def debug_request_context(ctx: Context) -> dict[str, Any]:
|
|
|
50
54
|
return {
|
|
51
55
|
"success": True,
|
|
52
56
|
"data": {
|
|
57
|
+
"server": {
|
|
58
|
+
"version": get_package_version(),
|
|
59
|
+
"cwd": os.getcwd(),
|
|
60
|
+
"argv": list(sys.argv),
|
|
61
|
+
},
|
|
53
62
|
"request_context": {
|
|
54
63
|
"client_id": rc_client_id,
|
|
55
64
|
"session_id": rc_session_id,
|
|
@@ -9,35 +9,41 @@ from typing import Annotated, Any, Literal
|
|
|
9
9
|
from fastmcp import Context
|
|
10
10
|
from services.registry import mcp_for_unity_tool
|
|
11
11
|
from services.tools import get_unity_instance_from_context
|
|
12
|
-
from services.tools.utils import parse_json_payload
|
|
12
|
+
from services.tools.utils import parse_json_payload, coerce_int
|
|
13
13
|
from transport.unity_transport import send_with_unity_instance
|
|
14
14
|
from transport.legacy.unity_connection import async_send_command_with_retry
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@mcp_for_unity_tool(
|
|
18
|
-
description=
|
|
18
|
+
description=(
|
|
19
|
+
"Performs asset operations (import, create, modify, delete, etc.) in Unity.\n\n"
|
|
20
|
+
"Tip (payload safety): for `action=\"search\"`, prefer paging (`page_size`, `page_number`) and keep "
|
|
21
|
+
"`generate_preview=false` (previews can add large base64 blobs)."
|
|
22
|
+
)
|
|
19
23
|
)
|
|
20
24
|
async def manage_asset(
|
|
21
25
|
ctx: Context,
|
|
22
26
|
action: Annotated[Literal["import", "create", "modify", "delete", "duplicate", "move", "rename", "search", "get_info", "create_folder", "get_components"], "Perform CRUD operations on assets."],
|
|
23
|
-
path: Annotated[str, "Asset path (e.g., 'Materials/MyMaterial.mat') or search scope."],
|
|
27
|
+
path: Annotated[str, "Asset path (e.g., 'Materials/MyMaterial.mat') or search scope (e.g., 'Assets')."],
|
|
24
28
|
asset_type: Annotated[str,
|
|
25
|
-
"Asset type (e.g., 'Material', 'Folder') - required for 'create'."] | None = None,
|
|
29
|
+
"Asset type (e.g., 'Material', 'Folder') - required for 'create'. Note: For ScriptableObjects, use manage_scriptable_object."] | None = None,
|
|
26
30
|
properties: Annotated[dict[str, Any] | str,
|
|
27
31
|
"Dictionary (or JSON string) of properties for 'create'/'modify'."] | None = None,
|
|
28
32
|
destination: Annotated[str,
|
|
29
33
|
"Target path for 'duplicate'/'move'."] | None = None,
|
|
30
34
|
generate_preview: Annotated[bool,
|
|
31
|
-
"Generate a preview/thumbnail for the asset when supported."
|
|
35
|
+
"Generate a preview/thumbnail for the asset when supported. "
|
|
36
|
+
"Warning: previews may include large base64 payloads; keep false unless needed."] = False,
|
|
32
37
|
search_pattern: Annotated[str,
|
|
33
|
-
"Search pattern (e.g., '*.prefab'
|
|
38
|
+
"Search pattern (e.g., '*.prefab' or AssetDatabase filters like 't:MonoScript'). "
|
|
39
|
+
"Recommended: put queries like 't:MonoScript' here and set path='Assets'."] | None = None,
|
|
34
40
|
filter_type: Annotated[str, "Filter type for search"] | None = None,
|
|
35
41
|
filter_date_after: Annotated[str,
|
|
36
42
|
"Date after which to filter"] | None = None,
|
|
37
43
|
page_size: Annotated[int | float | str,
|
|
38
|
-
"Page size for pagination"] | None = None,
|
|
44
|
+
"Page size for pagination. Recommended: 25 (smaller for LLM-friendly responses)."] | None = None,
|
|
39
45
|
page_number: Annotated[int | float | str,
|
|
40
|
-
"Page number for pagination"] | None = None,
|
|
46
|
+
"Page number for pagination (1-based)."] | None = None,
|
|
41
47
|
) -> dict[str, Any]:
|
|
42
48
|
unity_instance = get_unity_instance_from_context(ctx)
|
|
43
49
|
|
|
@@ -83,24 +89,32 @@ async def manage_asset(
|
|
|
83
89
|
await ctx.error(parse_error)
|
|
84
90
|
return {"success": False, "message": parse_error}
|
|
85
91
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
page_size = coerce_int(page_size)
|
|
93
|
+
page_number = coerce_int(page_number)
|
|
94
|
+
|
|
95
|
+
# --- Payload-safe normalization for common LLM mistakes (search) ---
|
|
96
|
+
# Unity's C# handler treats `path` as a folder scope. If a model mistakenly puts a query like
|
|
97
|
+
# "t:MonoScript" into `path`, Unity will consider it an invalid folder and fall back to searching
|
|
98
|
+
# the entire project, which is token-heavy. Normalize such cases into search_pattern + Assets scope.
|
|
99
|
+
action_l = (action or "").lower()
|
|
100
|
+
if action_l == "search":
|
|
90
101
|
try:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
raw_path = (path or "").strip()
|
|
103
|
+
except (AttributeError, TypeError):
|
|
104
|
+
# Handle case where path is not a string despite type annotation
|
|
105
|
+
raw_path = ""
|
|
106
|
+
|
|
107
|
+
# If the caller put an AssetDatabase query into `path`, treat it as `search_pattern`.
|
|
108
|
+
if (not search_pattern) and raw_path.startswith("t:"):
|
|
109
|
+
search_pattern = raw_path
|
|
110
|
+
path = "Assets"
|
|
111
|
+
await ctx.info("manage_asset(search): normalized query from `path` into `search_pattern` and set path='Assets'")
|
|
101
112
|
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
# If the caller used `asset_type` to mean a search filter, map it to filter_type.
|
|
114
|
+
# (In Unity, filterType becomes `t:<filterType>`.)
|
|
115
|
+
if (not filter_type) and asset_type and isinstance(asset_type, str):
|
|
116
|
+
filter_type = asset_type
|
|
117
|
+
await ctx.info("manage_asset(search): mapped `asset_type` into `filter_type` for safer server-side filtering")
|
|
104
118
|
|
|
105
119
|
# Prepare parameters for the C# handler
|
|
106
120
|
params_dict = {
|
|
@@ -7,7 +7,7 @@ from services.registry import mcp_for_unity_tool
|
|
|
7
7
|
from services.tools import get_unity_instance_from_context
|
|
8
8
|
from transport.unity_transport import send_with_unity_instance
|
|
9
9
|
from transport.legacy.unity_connection import async_send_command_with_retry
|
|
10
|
-
from services.tools.utils import coerce_bool, parse_json_payload
|
|
10
|
+
from services.tools.utils import coerce_bool, parse_json_payload, coerce_int
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@mcp_for_unity_tool(
|
|
@@ -68,6 +68,11 @@ async def manage_gameobject(
|
|
|
68
68
|
# Controls whether serialization of private [SerializeField] fields is included
|
|
69
69
|
includeNonPublicSerialized: Annotated[bool | str,
|
|
70
70
|
"Controls whether serialization of private [SerializeField] fields is included (accepts true/false or 'true'/'false')"] | None = None,
|
|
71
|
+
# --- Paging/safety for get_components ---
|
|
72
|
+
page_size: Annotated[int | str, "Page size for get_components paging."] | None = None,
|
|
73
|
+
cursor: Annotated[int | str, "Opaque cursor for get_components paging (offset)."] | None = None,
|
|
74
|
+
max_components: Annotated[int | str, "Hard cap on returned components per request (safety)."] | None = None,
|
|
75
|
+
include_properties: Annotated[bool | str, "If true, include serialized component properties (bounded)."] | None = None,
|
|
71
76
|
# --- Parameters for 'duplicate' ---
|
|
72
77
|
new_name: Annotated[str,
|
|
73
78
|
"New name for the duplicated object (default: SourceName_Copy)"] | None = None,
|
|
@@ -134,7 +139,12 @@ async def manage_gameobject(
|
|
|
134
139
|
search_in_children = coerce_bool(search_in_children)
|
|
135
140
|
search_inactive = coerce_bool(search_inactive)
|
|
136
141
|
includeNonPublicSerialized = coerce_bool(includeNonPublicSerialized)
|
|
142
|
+
include_properties = coerce_bool(include_properties)
|
|
137
143
|
world_space = coerce_bool(world_space, default=True)
|
|
144
|
+
# If coercion fails, omit these fields (None) rather than preserving invalid input.
|
|
145
|
+
page_size = coerce_int(page_size, default=None)
|
|
146
|
+
cursor = coerce_int(cursor, default=None)
|
|
147
|
+
max_components = coerce_int(max_components, default=None)
|
|
138
148
|
|
|
139
149
|
# Coerce 'component_properties' from JSON string to dict for client compatibility
|
|
140
150
|
component_properties = parse_json_payload(component_properties)
|
|
@@ -194,6 +204,10 @@ async def manage_gameobject(
|
|
|
194
204
|
"searchInactive": search_inactive,
|
|
195
205
|
"componentName": component_name,
|
|
196
206
|
"includeNonPublicSerialized": includeNonPublicSerialized,
|
|
207
|
+
"pageSize": page_size,
|
|
208
|
+
"cursor": cursor,
|
|
209
|
+
"maxComponents": max_components,
|
|
210
|
+
"includeProperties": include_properties,
|
|
197
211
|
# Parameters for 'duplicate'
|
|
198
212
|
"new_name": new_name,
|
|
199
213
|
"offset": offset,
|
|
@@ -3,6 +3,7 @@ from typing import Annotated, Literal, Any
|
|
|
3
3
|
from fastmcp import Context
|
|
4
4
|
from services.registry import mcp_for_unity_tool
|
|
5
5
|
from services.tools import get_unity_instance_from_context
|
|
6
|
+
from services.tools.utils import coerce_int, coerce_bool
|
|
6
7
|
from transport.unity_transport import send_with_unity_instance
|
|
7
8
|
from transport.legacy.unity_connection import async_send_command_with_retry
|
|
8
9
|
|
|
@@ -27,29 +28,27 @@ async def manage_scene(
|
|
|
27
28
|
"Unity build index (quote as string, e.g., '0')."] | None = None,
|
|
28
29
|
screenshot_file_name: Annotated[str, "Screenshot file name (optional). Defaults to timestamp when omitted."] | None = None,
|
|
29
30
|
screenshot_super_size: Annotated[int | str, "Screenshot supersize multiplier (integer ≥1). Optional." ] | None = None,
|
|
31
|
+
# --- get_hierarchy paging/safety ---
|
|
32
|
+
parent: Annotated[str | int, "Optional parent GameObject reference (name/path/instanceID) to list direct children."] | None = None,
|
|
33
|
+
page_size: Annotated[int | str, "Page size for get_hierarchy paging."] | None = None,
|
|
34
|
+
cursor: Annotated[int | str, "Opaque cursor for paging (offset)."] | None = None,
|
|
35
|
+
max_nodes: Annotated[int | str, "Hard cap on returned nodes per request (safety)."] | None = None,
|
|
36
|
+
max_depth: Annotated[int | str, "Accepted for forward-compatibility; current paging returns a single level."] | None = None,
|
|
37
|
+
max_children_per_node: Annotated[int | str, "Child paging hint (safety)."] | None = None,
|
|
38
|
+
include_transform: Annotated[bool | str, "If true, include local transform in node summaries."] | None = None,
|
|
30
39
|
) -> dict[str, Any]:
|
|
31
40
|
# Get active instance from session state
|
|
32
41
|
# Removed session_state import
|
|
33
42
|
unity_instance = get_unity_instance_from_context(ctx)
|
|
34
43
|
try:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return int(value)
|
|
44
|
-
s = str(value).strip()
|
|
45
|
-
if s.lower() in ("", "none", "null"):
|
|
46
|
-
return default
|
|
47
|
-
return int(float(s))
|
|
48
|
-
except Exception:
|
|
49
|
-
return default
|
|
50
|
-
|
|
51
|
-
coerced_build_index = _coerce_int(build_index, default=None)
|
|
52
|
-
coerced_super_size = _coerce_int(screenshot_super_size, default=None)
|
|
44
|
+
coerced_build_index = coerce_int(build_index, default=None)
|
|
45
|
+
coerced_super_size = coerce_int(screenshot_super_size, default=None)
|
|
46
|
+
coerced_page_size = coerce_int(page_size, default=None)
|
|
47
|
+
coerced_cursor = coerce_int(cursor, default=None)
|
|
48
|
+
coerced_max_nodes = coerce_int(max_nodes, default=None)
|
|
49
|
+
coerced_max_depth = coerce_int(max_depth, default=None)
|
|
50
|
+
coerced_max_children_per_node = coerce_int(max_children_per_node, default=None)
|
|
51
|
+
coerced_include_transform = coerce_bool(include_transform, default=None)
|
|
53
52
|
|
|
54
53
|
params: dict[str, Any] = {"action": action}
|
|
55
54
|
if name:
|
|
@@ -62,6 +61,22 @@ async def manage_scene(
|
|
|
62
61
|
params["fileName"] = screenshot_file_name
|
|
63
62
|
if coerced_super_size is not None:
|
|
64
63
|
params["superSize"] = coerced_super_size
|
|
64
|
+
|
|
65
|
+
# get_hierarchy paging/safety params (optional)
|
|
66
|
+
if parent is not None:
|
|
67
|
+
params["parent"] = parent
|
|
68
|
+
if coerced_page_size is not None:
|
|
69
|
+
params["pageSize"] = coerced_page_size
|
|
70
|
+
if coerced_cursor is not None:
|
|
71
|
+
params["cursor"] = coerced_cursor
|
|
72
|
+
if coerced_max_nodes is not None:
|
|
73
|
+
params["maxNodes"] = coerced_max_nodes
|
|
74
|
+
if coerced_max_depth is not None:
|
|
75
|
+
params["maxDepth"] = coerced_max_depth
|
|
76
|
+
if coerced_max_children_per_node is not None:
|
|
77
|
+
params["maxChildrenPerNode"] = coerced_max_children_per_node
|
|
78
|
+
if coerced_include_transform is not None:
|
|
79
|
+
params["includeTransform"] = coerced_include_transform
|
|
65
80
|
|
|
66
81
|
# Use centralized retry helper with instance routing
|
|
67
82
|
response = await send_with_unity_instance(async_send_command_with_retry, unity_instance, "manage_scene", params)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool wrapper for managing ScriptableObject assets via Unity MCP.
|
|
3
|
+
|
|
4
|
+
Unity-side handler: MCPForUnity.Editor.Tools.ManageScriptableObject
|
|
5
|
+
Command name: "manage_scriptable_object"
|
|
6
|
+
Actions:
|
|
7
|
+
- create: create an SO asset (optionally with patches)
|
|
8
|
+
- modify: apply serialized property patches to an existing SO asset
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Annotated, Any, Literal
|
|
14
|
+
|
|
15
|
+
from fastmcp import Context
|
|
16
|
+
|
|
17
|
+
from services.registry import mcp_for_unity_tool
|
|
18
|
+
from services.tools import get_unity_instance_from_context
|
|
19
|
+
from services.tools.utils import coerce_bool, parse_json_payload
|
|
20
|
+
from transport.unity_transport import send_with_unity_instance
|
|
21
|
+
from transport.legacy.unity_connection import async_send_command_with_retry
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@mcp_for_unity_tool(
|
|
25
|
+
description="Creates and modifies ScriptableObject assets using Unity SerializedObject property paths."
|
|
26
|
+
)
|
|
27
|
+
async def manage_scriptable_object(
|
|
28
|
+
ctx: Context,
|
|
29
|
+
action: Annotated[Literal["create", "modify"], "Action to perform: create or modify."],
|
|
30
|
+
# --- create params ---
|
|
31
|
+
type_name: Annotated[str | None, "Namespace-qualified ScriptableObject type name (for create)."] = None,
|
|
32
|
+
folder_path: Annotated[str | None, "Target folder under Assets/... (for create)."] = None,
|
|
33
|
+
asset_name: Annotated[str | None, "Asset file name without extension (for create)."] = None,
|
|
34
|
+
overwrite: Annotated[bool | str | None, "If true, overwrite existing asset at same path (for create)."] = None,
|
|
35
|
+
# --- modify params ---
|
|
36
|
+
target: Annotated[dict[str, Any] | str | None, "Target asset reference {guid|path} (for modify)."] = None,
|
|
37
|
+
# --- shared ---
|
|
38
|
+
patches: Annotated[list[dict[str, Any]] | str | None, "Patch list (or JSON string) to apply."] = None,
|
|
39
|
+
) -> dict[str, Any]:
|
|
40
|
+
unity_instance = get_unity_instance_from_context(ctx)
|
|
41
|
+
|
|
42
|
+
# Tolerate JSON-string payloads (LLMs sometimes stringify complex objects)
|
|
43
|
+
parsed_target = parse_json_payload(target)
|
|
44
|
+
parsed_patches = parse_json_payload(patches)
|
|
45
|
+
|
|
46
|
+
if parsed_target is not None and not isinstance(parsed_target, dict):
|
|
47
|
+
return {"success": False, "message": "manage_scriptable_object: 'target' must be an object {guid|path} (or JSON string of such)."}
|
|
48
|
+
|
|
49
|
+
if parsed_patches is not None and not isinstance(parsed_patches, list):
|
|
50
|
+
return {"success": False, "message": "manage_scriptable_object: 'patches' must be a list (or JSON string of a list)."}
|
|
51
|
+
|
|
52
|
+
params: dict[str, Any] = {
|
|
53
|
+
"action": action,
|
|
54
|
+
"typeName": type_name,
|
|
55
|
+
"folderPath": folder_path,
|
|
56
|
+
"assetName": asset_name,
|
|
57
|
+
"overwrite": coerce_bool(overwrite, default=None),
|
|
58
|
+
"target": parsed_target,
|
|
59
|
+
"patches": parsed_patches,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Remove None values to keep Unity handler simpler
|
|
63
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
64
|
+
|
|
65
|
+
response = await send_with_unity_instance(
|
|
66
|
+
async_send_command_with_retry,
|
|
67
|
+
unity_instance,
|
|
68
|
+
"manage_scriptable_object",
|
|
69
|
+
params,
|
|
70
|
+
)
|
|
71
|
+
await ctx.info(f"Response {response}")
|
|
72
|
+
return response if isinstance(response, dict) else {"success": False, "message": "Unexpected response from Unity."}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
@@ -6,6 +6,7 @@ from typing import Annotated, Any, Literal
|
|
|
6
6
|
from fastmcp import Context
|
|
7
7
|
from services.registry import mcp_for_unity_tool
|
|
8
8
|
from services.tools import get_unity_instance_from_context
|
|
9
|
+
from services.tools.utils import coerce_int, coerce_bool
|
|
9
10
|
from transport.unity_transport import send_with_unity_instance
|
|
10
11
|
from transport.legacy.unity_connection import async_send_command_with_retry
|
|
11
12
|
|
|
@@ -38,42 +39,14 @@ async def read_console(
|
|
|
38
39
|
format = format if format is not None else 'detailed'
|
|
39
40
|
# Coerce booleans defensively (strings like 'true'/'false')
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
if value is None:
|
|
43
|
-
return default
|
|
44
|
-
if isinstance(value, bool):
|
|
45
|
-
return value
|
|
46
|
-
if isinstance(value, str):
|
|
47
|
-
v = value.strip().lower()
|
|
48
|
-
if v in ("true", "1", "yes", "on"):
|
|
49
|
-
return True
|
|
50
|
-
if v in ("false", "0", "no", "off"):
|
|
51
|
-
return False
|
|
52
|
-
return bool(value)
|
|
53
|
-
|
|
54
|
-
include_stacktrace = _coerce_bool(include_stacktrace, True)
|
|
42
|
+
include_stacktrace = coerce_bool(include_stacktrace, default=True)
|
|
55
43
|
|
|
56
44
|
# Normalize action if it's a string
|
|
57
45
|
if isinstance(action, str):
|
|
58
46
|
action = action.lower()
|
|
59
47
|
|
|
60
48
|
# Coerce count defensively (string/float -> int)
|
|
61
|
-
|
|
62
|
-
if value is None:
|
|
63
|
-
return default
|
|
64
|
-
try:
|
|
65
|
-
if isinstance(value, bool):
|
|
66
|
-
return default
|
|
67
|
-
if isinstance(value, int):
|
|
68
|
-
return int(value)
|
|
69
|
-
s = str(value).strip()
|
|
70
|
-
if s.lower() in ("", "none", "null"):
|
|
71
|
-
return default
|
|
72
|
-
return int(float(s))
|
|
73
|
-
except Exception:
|
|
74
|
-
return default
|
|
75
|
-
|
|
76
|
-
count = _coerce_int(count)
|
|
49
|
+
count = coerce_int(count)
|
|
77
50
|
|
|
78
51
|
# Prepare parameters for the C# handler
|
|
79
52
|
params_dict = {
|
|
@@ -7,6 +7,7 @@ from pydantic import BaseModel, Field
|
|
|
7
7
|
from models import MCPResponse
|
|
8
8
|
from services.registry import mcp_for_unity_tool
|
|
9
9
|
from services.tools import get_unity_instance_from_context
|
|
10
|
+
from services.tools.utils import coerce_int
|
|
10
11
|
from transport.unity_transport import send_with_unity_instance
|
|
11
12
|
from transport.legacy.unity_connection import async_send_command_with_retry
|
|
12
13
|
|
|
@@ -54,22 +55,6 @@ async def run_tests(
|
|
|
54
55
|
) -> RunTestsResponse:
|
|
55
56
|
unity_instance = get_unity_instance_from_context(ctx)
|
|
56
57
|
|
|
57
|
-
# Coerce timeout defensively (string/float -> int)
|
|
58
|
-
def _coerce_int(value, default=None):
|
|
59
|
-
if value is None:
|
|
60
|
-
return default
|
|
61
|
-
try:
|
|
62
|
-
if isinstance(value, bool):
|
|
63
|
-
return default
|
|
64
|
-
if isinstance(value, int):
|
|
65
|
-
return int(value)
|
|
66
|
-
s = str(value).strip()
|
|
67
|
-
if s.lower() in ("", "none", "null"):
|
|
68
|
-
return default
|
|
69
|
-
return int(float(s))
|
|
70
|
-
except Exception:
|
|
71
|
-
return default
|
|
72
|
-
|
|
73
58
|
# Coerce string or list to list of strings
|
|
74
59
|
def _coerce_string_list(value) -> list[str] | None:
|
|
75
60
|
if value is None:
|
|
@@ -82,7 +67,7 @@ async def run_tests(
|
|
|
82
67
|
return None
|
|
83
68
|
|
|
84
69
|
params: dict[str, Any] = {"mode": mode}
|
|
85
|
-
ts =
|
|
70
|
+
ts = coerce_int(timeout_seconds)
|
|
86
71
|
if ts is not None:
|
|
87
72
|
params["timeoutSeconds"] = ts
|
|
88
73
|
|
|
@@ -58,3 +58,20 @@ def parse_json_payload(value: Any) -> Any:
|
|
|
58
58
|
except (json.JSONDecodeError, ValueError):
|
|
59
59
|
# If parsing fails, assume it was meant to be a literal string
|
|
60
60
|
return value
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def coerce_int(value: Any, default: int | None = None) -> int | None:
|
|
64
|
+
"""Attempt to coerce a loosely-typed value to an integer."""
|
|
65
|
+
if value is None:
|
|
66
|
+
return default
|
|
67
|
+
try:
|
|
68
|
+
if isinstance(value, bool):
|
|
69
|
+
return default
|
|
70
|
+
if isinstance(value, int):
|
|
71
|
+
return value
|
|
72
|
+
s = str(value).strip()
|
|
73
|
+
if s.lower() in ("", "none", "null"):
|
|
74
|
+
return default
|
|
75
|
+
return int(float(s))
|
|
76
|
+
except Exception:
|
|
77
|
+
return default
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/registry/resource_registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/resources/unity_instances.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/execute_custom_tool.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/script_apply_edits.py
RENAMED
|
File without changes
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/services/tools/set_active_instance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/legacy/stdio_port_registry.py
RENAMED
|
File without changes
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/legacy/unity_connection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcpforunityserver-8.2.3 → mcpforunityserver-8.5.0}/src/transport/unity_instance_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|