Skip to content

Commit 0f506e4

Browse files
authored
Copy the MCP server to the top level (#354)
I want to experiment with using `uvx` from this location, and if it manages all the use cases correctly, we won't clone and copy the server code
1 parent 697e0fb commit 0f506e4

36 files changed

+6318
-0
lines changed

Server/Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM python:3.13-slim
2+
3+
RUN apt-get update && apt-get install -y --no-install-recommends \
4+
git \
5+
&& rm -rf /var/lib/apt/lists/*
6+
7+
WORKDIR /app
8+
9+
RUN pip install uv
10+
11+
COPY . /app
12+
13+
RUN uv sync
14+
15+
CMD ["uv", "run", "server.py"]

Server/README.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# MCP for Unity Server
2+
3+
[![MCP](https://badge.mcpx.dev?status=on 'MCP Enabled')](https://modelcontextprotocol.io/introduction)
4+
[![python](https://img.shields.io/badge/Python-3.11+-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org)
5+
[![License](https://img.shields.io/badge/License-MIT-red.svg 'MIT License')](https://opensource.org/licenses/MIT)
6+
[![Discord](https://img.shields.io/badge/discord-join-red.svg?logo=discord&logoColor=white)](https://discord.gg/y4p8KfzrN4)
7+
8+
Model Context Protocol server for Unity Editor integration. Control Unity through natural language using AI assistants like Claude, Cursor, and more.
9+
10+
**Maintained by [Coplay](https://www.coplay.dev/?ref=unity-mcp)** - This project is not affiliated with Unity Technologies.
11+
12+
💬 **Join our community:** [Discord Server](https://discord.gg/y4p8KfzrN4)
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.
15+
16+
---
17+
18+
## Installation
19+
20+
### Option 1: Using uvx (Recommended)
21+
22+
Run directly from GitHub without installation:
23+
24+
```bash
25+
uvx --from git+https://github.com/CoplayDev/unity-mcp@v6.3.0#subdirectory=Server mcp-for-unity
26+
```
27+
28+
**MCP Client Configuration:**
29+
30+
```json
31+
{
32+
"mcpServers": {
33+
"UnityMCP": {
34+
"command": "uvx",
35+
"args": [
36+
"--from",
37+
"git+https://github.com/CoplayDev/unity-mcp@v6.3.0#subdirectory=Server",
38+
"mcp-for-unity"
39+
]
40+
}
41+
}
42+
}
43+
```
44+
45+
### Option 2: Using uv (Local Installation)
46+
47+
For local development or custom installations:
48+
49+
```bash
50+
# Clone the repository
51+
git clone https://github.com/CoplayDev/unity-mcp.git
52+
cd unity-mcp/Server
53+
54+
# Run with uv
55+
uv run server.py
56+
```
57+
58+
**MCP Client Configuration:**
59+
60+
**Windows:**
61+
```json
62+
{
63+
"mcpServers": {
64+
"UnityMCP": {
65+
"command": "uv",
66+
"args": [
67+
"run",
68+
"--directory",
69+
"C:\\path\\to\\unity-mcp\\Server",
70+
"server.py"
71+
]
72+
}
73+
}
74+
}
75+
```
76+
77+
**macOS/Linux:**
78+
```json
79+
{
80+
"mcpServers": {
81+
"UnityMCP": {
82+
"command": "uv",
83+
"args": [
84+
"run",
85+
"--directory",
86+
"/path/to/unity-mcp/Server",
87+
"server.py"
88+
]
89+
}
90+
}
91+
}
92+
```
93+
94+
### Option 3: Using Docker
95+
96+
```bash
97+
docker build -t unity-mcp-server .
98+
docker run unity-mcp-server
99+
```
100+
101+
**MCP Client Configuration:**
102+
103+
```json
104+
{
105+
"mcpServers": {
106+
"UnityMCP": {
107+
"command": "docker",
108+
"args": ["run", "-i", "unity-mcp-server"]
109+
}
110+
}
111+
}
112+
```
113+
114+
---
115+
116+
## Configuration
117+
118+
The server connects to Unity Editor automatically when both are running. No additional configuration needed.
119+
120+
**Environment Variables:**
121+
122+
- `DISABLE_TELEMETRY=true` - Opt out of anonymous usage analytics
123+
- `LOG_LEVEL=DEBUG` - Enable detailed logging (default: INFO)
124+
125+
---
126+
127+
## Example Prompts
128+
129+
Once connected, try these commands in your AI assistant:
130+
131+
- "Create a 3D player controller with WASD movement"
132+
- "Add a rotating cube to the scene with a red material"
133+
- "Create a simple platformer level with obstacles"
134+
- "Generate a shader that creates a holographic effect"
135+
- "List all GameObjects in the current scene"
136+
137+
---
138+
139+
## Documentation
140+
141+
For complete documentation, troubleshooting, and advanced usage:
142+
143+
📖 **[Full Documentation](https://github.com/CoplayDev/unity-mcp#readme)**
144+
145+
---
146+
147+
## Requirements
148+
149+
- **Python:** 3.11 or newer
150+
- **Unity Editor:** 2021.3 LTS or newer
151+
- **uv:** Python package manager ([Installation Guide](https://docs.astral.sh/uv/getting-started/installation/))
152+
153+
---
154+
155+
## License
156+
157+
MIT License - See [LICENSE](https://github.com/CoplayDev/unity-mcp/blob/main/LICENSE)

Server/__init__.py

Whitespace-only changes.

Server/config.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""
2+
Configuration settings for the MCP for Unity Server.
3+
This file contains all configurable parameters for the server.
4+
"""
5+
6+
from dataclasses import dataclass
7+
8+
9+
@dataclass
10+
class ServerConfig:
11+
"""Main configuration class for the MCP server."""
12+
13+
# Network settings
14+
unity_host: str = "localhost"
15+
unity_port: int = 6400
16+
mcp_port: int = 6500
17+
18+
# Connection settings
19+
connection_timeout: float = 30.0
20+
buffer_size: int = 16 * 1024 * 1024 # 16MB buffer
21+
# Framed receive behavior
22+
# max seconds to wait while consuming heartbeats only
23+
framed_receive_timeout: float = 2.0
24+
# cap heartbeat frames consumed before giving up
25+
max_heartbeat_frames: int = 16
26+
27+
# Logging settings
28+
log_level: str = "INFO"
29+
log_format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
30+
31+
# Server settings
32+
max_retries: int = 5
33+
retry_delay: float = 0.25
34+
# Backoff hint returned to clients when Unity is reloading (milliseconds)
35+
reload_retry_ms: int = 250
36+
# Number of polite retries when Unity reports reloading
37+
# 40 × 250ms ≈ 10s default window
38+
reload_max_retries: int = 40
39+
40+
# Telemetry settings
41+
telemetry_enabled: bool = True
42+
# Align with telemetry.py default Cloud Run endpoint
43+
telemetry_endpoint: str = "https://api-prod.coplay.dev/telemetry/events"
44+
45+
46+
# Create a global config instance
47+
config = ServerConfig()

Server/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from typing import Any
2+
from pydantic import BaseModel
3+
4+
5+
class MCPResponse(BaseModel):
6+
success: bool
7+
message: str | None = None
8+
error: str | None = None
9+
data: Any | None = None

Server/module_discovery.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""
2+
Shared module discovery utilities for auto-registering tools and resources.
3+
"""
4+
import importlib
5+
import logging
6+
from pathlib import Path
7+
import pkgutil
8+
from typing import Generator
9+
10+
logger = logging.getLogger("mcp-for-unity-server")
11+
12+
13+
def discover_modules(base_dir: Path, package_name: str) -> Generator[str, None, None]:
14+
"""
15+
Discover and import all Python modules in a directory and its subdirectories.
16+
17+
Args:
18+
base_dir: The base directory to search for modules
19+
package_name: The package name to use for relative imports (e.g., 'tools' or 'resources')
20+
21+
Yields:
22+
Full module names that were successfully imported
23+
"""
24+
# Discover modules in the top level
25+
for _, module_name, _ in pkgutil.iter_modules([str(base_dir)]):
26+
# Skip private modules and __init__
27+
if module_name.startswith('_'):
28+
continue
29+
30+
try:
31+
full_module_name = f'.{module_name}'
32+
importlib.import_module(full_module_name, package_name)
33+
yield full_module_name
34+
except Exception as e:
35+
logger.warning(f"Failed to import module {module_name}: {e}")
36+
37+
# Discover modules in subdirectories (one level deep)
38+
for subdir in base_dir.iterdir():
39+
if not subdir.is_dir() or subdir.name.startswith('_') or subdir.name.startswith('.'):
40+
continue
41+
42+
# Check if subdirectory contains Python modules
43+
for _, module_name, _ in pkgutil.iter_modules([str(subdir)]):
44+
# Skip private modules and __init__
45+
if module_name.startswith('_'):
46+
continue
47+
48+
try:
49+
# Import as package.subdirname.modulename
50+
full_module_name = f'.{subdir.name}.{module_name}'
51+
importlib.import_module(full_module_name, package_name)
52+
yield full_module_name
53+
except Exception as e:
54+
logger.warning(
55+
f"Failed to import module {subdir.name}.{module_name}: {e}")

0 commit comments

Comments
 (0)