Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
07932ea
refactor: migrate command routing to use CommandRegistry lookup inste…
msanatan Sep 27, 2025
d7dd309
style: improve code formatting and indentation consistency
msanatan Sep 27, 2025
b3338e7
refactor: clean up imports and type hints across tool modules
msanatan Sep 27, 2025
a967d67
Revert "feat: Implement Asset Store Compliance for Unity MCP Bridge"
msanatan Sep 27, 2025
29a3f3a
Revert "feat(asset-store): implement post-installation prompt system …
msanatan Sep 27, 2025
6041fc3
Merge branch 'main' into standardize-mcp-tools
msanatan Sep 27, 2025
b6fb5a7
chore: upgrade mcp[cli] dependency from 1.4.1 to 1.15.0
msanatan Sep 27, 2025
5b8d1e3
style: fix formatting and whitespace in Python server files
msanatan Sep 27, 2025
749fea4
Remove description, probably a Python versionn change
msanatan Sep 27, 2025
b94dcca
feat: add type hints and parameter descriptions to Unity MCP tools
msanatan Sep 27, 2025
b31ffd1
docs: improve shader management tool parameter descriptions and types
msanatan Sep 27, 2025
4dfb167
refactor: add type annotations and improve documentation for script m…
msanatan Sep 27, 2025
966f957
refactor: improve type annotations and documentation in manage_scene …
msanatan Sep 27, 2025
01b2a33
refactor: add type annotations and improve parameter descriptions acr…
msanatan Sep 27, 2025
a35edf8
Merge remote-tracking branch 'upstream/main'
msanatan Sep 27, 2025
1dbb527
Merge branch 'main' into standardize-mcp-tools
msanatan Sep 27, 2025
4ca2529
feat: add explicit name parameters to all MCP tool decorators
msanatan Sep 27, 2025
fe69e61
refactor: remove unused Unity connection instance in manage_asset_tools
msanatan Sep 27, 2025
ae4d401
chore: update type hints in manage_editor function parameters for bet…
msanatan Sep 27, 2025
8375648
feat: make name and path parameters optional for scene management ope…
msanatan Sep 27, 2025
32243de
refactor: remove unused get_unity_connection import from manage_asset.py
msanatan Sep 27, 2025
a7af7b4
chore: rename Operation parameter annotation to Operations for consis…
msanatan Sep 27, 2025
4395ec5
feat: add logging to MCP clients for tool actions across MCP server c…
msanatan Sep 27, 2025
226221e
chore: add FastMCP type hint to register_all_tools parameter
msanatan Sep 27, 2025
f66e1fe
style: reformat docstring in apply_text_edits tool to use multiline s…
msanatan Sep 27, 2025
6addf3a
refactor: update type hints from Dict/List/Tuple/Optional to modern P…
msanatan Sep 27, 2025
29ad9d5
refactor: clean up imports and add type annotations to script editing…
msanatan Sep 27, 2025
b7d669f
refactor: update type hints to use | None syntax for optional parameters
msanatan Sep 27, 2025
8cd7fba
Minor fixes
msanatan Sep 27, 2025
d1691a7
docs: improve tool descriptions with clearer action explanations
msanatan Sep 27, 2025
679da41
refactor: remove legacy update action migration code from manage_scri…
msanatan Sep 27, 2025
e338c7a
style: replace em dashes with regular hyphens in tool descriptions [s…
msanatan Sep 27, 2025
4d50584
refactor: convert manage_script_capabilities docstring to multiline f…
msanatan Sep 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions UnityMcpBridge/Editor/MCPForUnityBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1040,27 +1040,7 @@ private static string ExecuteCommand(Command command)

// Use JObject for parameters as the new handlers likely expect this
JObject paramsObject = command.@params ?? new JObject();

// Route command based on the new tool structure from the refactor plan
object result = command.type switch
{
// Maps the command type (tool name) to the corresponding handler's static HandleCommand method
// Assumes each handler class has a static method named 'HandleCommand' that takes JObject parameters
"manage_script" => ManageScript.HandleCommand(paramsObject),
// Run scene operations on the main thread to avoid deadlocks/hangs (with diagnostics under debug flag)
"manage_scene" => HandleManageScene(paramsObject)
?? throw new TimeoutException($"manage_scene timed out after {FrameIOTimeoutMs} ms on main thread"),
"manage_editor" => ManageEditor.HandleCommand(paramsObject),
"manage_gameobject" => ManageGameObject.HandleCommand(paramsObject),
"manage_asset" => ManageAsset.HandleCommand(paramsObject),
"manage_shader" => ManageShader.HandleCommand(paramsObject),
"read_console" => ReadConsole.HandleCommand(paramsObject),
"manage_menu_item" => ManageMenuItem.HandleCommand(paramsObject),
"manage_prefabs" => ManagePrefabs.HandleCommand(paramsObject),
_ => throw new ArgumentException(
$"Unknown or unsupported command type: {command.type}"
),
};
object result = CommandRegistry.GetHandler(command.type)(paramsObject);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed in an earlier PR, it probably re-appeared by a bad merge conflict resolution


// Standard success response format
var response = new { status = "success", result };
Expand Down
2 changes: 2 additions & 0 deletions UnityMcpBridge/Editor/Tools/CommandRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using MCPForUnity.Editor.Tools.MenuItems;
using MCPForUnity.Editor.Tools.Prefabs;

namespace MCPForUnity.Editor.Tools
{
Expand All @@ -22,6 +23,7 @@ public static class CommandRegistry
{ "read_console", ReadConsole.HandleCommand },
{ "manage_menu_item", ManageMenuItem.HandleCommand },
{ "manage_shader", ManageShader.HandleCommand},
{ "manage_prefabs", ManagePrefabs.HandleCommand},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue with earlier merge, this should have been in

};

/// <summary>
Expand Down
588 changes: 295 additions & 293 deletions UnityMcpBridge/Editor/Tools/ManageScript.cs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whitespace changes

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion UnityMcpBridge/UnityMcpServer~/src/Dockerfile
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whitespace changes

Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ RUN uv pip install --system -e .


# Command to run the server
CMD ["uv", "run", "server.py"]
CMD ["uv", "run", "server.py"]
2 changes: 1 addition & 1 deletion UnityMcpBridge/UnityMcpServer~/src/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""
MCP for Unity Server package.
"""
"""
23 changes: 14 additions & 9 deletions UnityMcpBridge/UnityMcpServer~/src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,30 @@

from dataclasses import dataclass


@dataclass
class ServerConfig:
"""Main configuration class for the MCP server."""

# Network settings
unity_host: str = "localhost"
unity_port: int = 6400
mcp_port: int = 6500

# Connection settings
connection_timeout: float = 1.0 # short initial timeout; retries use shorter timeouts
# short initial timeout; retries use shorter timeouts
connection_timeout: float = 1.0
buffer_size: int = 16 * 1024 * 1024 # 16MB buffer
# Framed receive behavior
framed_receive_timeout: float = 2.0 # max seconds to wait while consuming heartbeats only
max_heartbeat_frames: int = 16 # cap heartbeat frames consumed before giving up

# max seconds to wait while consuming heartbeats only
framed_receive_timeout: float = 2.0
# cap heartbeat frames consumed before giving up
max_heartbeat_frames: int = 16

# Logging settings
log_level: str = "INFO"
log_format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"

# Server settings
max_retries: int = 10
retry_delay: float = 0.25
Expand All @@ -33,11 +37,12 @@ class ServerConfig:
# Number of polite retries when Unity reports reloading
# 40 × 250ms ≈ 10s default window
reload_max_retries: int = 40

# Telemetry settings
telemetry_enabled: bool = True
# Align with telemetry.py default Cloud Run endpoint
telemetry_endpoint: str = "https://api-prod.coplay.dev/telemetry/events"


# Create a global config instance
config = ServerConfig()
config = ServerConfig()
39 changes: 22 additions & 17 deletions UnityMcpBridge/UnityMcpServer~/src/port_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,31 @@
(quick socket connect + ping) before choosing it.
"""

import glob
import json
import os
import logging
from pathlib import Path
from typing import Optional, List
import glob
import socket
from typing import Optional, List

logger = logging.getLogger("mcp-for-unity-server")


class PortDiscovery:
"""Handles port discovery from Unity Bridge registry"""
REGISTRY_FILE = "unity-mcp-port.json" # legacy single-project file
DEFAULT_PORT = 6400
CONNECT_TIMEOUT = 0.3 # seconds, keep this snappy during discovery

@staticmethod
def get_registry_path() -> Path:
"""Get the path to the port registry file"""
return Path.home() / ".unity-mcp" / PortDiscovery.REGISTRY_FILE

@staticmethod
def get_registry_dir() -> Path:
return Path.home() / ".unity-mcp"

@staticmethod
def list_candidate_files() -> List[Path]:
"""Return candidate registry files, newest first.
Expand All @@ -52,7 +52,7 @@ def list_candidate_files() -> List[Path]:
# Put legacy at the end so hashed, per-project files win
hashed.append(legacy)
return hashed

@staticmethod
def _try_probe_unity_mcp(port: int) -> bool:
"""Quickly check if a MCP for Unity listener is on this port.
Expand All @@ -78,7 +78,8 @@ def _read_latest_status() -> Optional[dict]:
try:
base = PortDiscovery.get_registry_dir()
status_files = sorted(
(Path(p) for p in glob.glob(str(base / "unity-mcp-status-*.json"))),
(Path(p)
for p in glob.glob(str(base / "unity-mcp-status-*.json"))),
key=lambda p: p.stat().st_mtime,
reverse=True,
)
Expand All @@ -88,14 +89,14 @@ def _read_latest_status() -> Optional[dict]:
return json.load(f)
except Exception:
return None

@staticmethod
def discover_unity_port() -> int:
"""
Discover Unity port by scanning per-project and legacy registry files.
Prefer the newest file whose port responds; fall back to first parsed
value; finally default to 6400.

Returns:
Port number to connect to
"""
Expand All @@ -120,26 +121,29 @@ def discover_unity_port() -> int:
if first_seen_port is None:
first_seen_port = unity_port
if PortDiscovery._try_probe_unity_mcp(unity_port):
logger.info(f"Using Unity port from {path.name}: {unity_port}")
logger.info(
f"Using Unity port from {path.name}: {unity_port}")
return unity_port
except Exception as e:
logger.warning(f"Could not read port registry {path}: {e}")

if first_seen_port is not None:
logger.info(f"No responsive port found; using first seen value {first_seen_port}")
logger.info(
f"No responsive port found; using first seen value {first_seen_port}")
return first_seen_port

# Fallback to default port
logger.info(f"No port registry found; using default port {PortDiscovery.DEFAULT_PORT}")
logger.info(
f"No port registry found; using default port {PortDiscovery.DEFAULT_PORT}")
return PortDiscovery.DEFAULT_PORT

@staticmethod
def get_port_config() -> Optional[dict]:
"""
Get the most relevant port configuration from registry.
Returns the most recent hashed file's config if present,
otherwise the legacy file's config. Returns None if nothing exists.

Returns:
Port configuration dict or None if not found
"""
Expand All @@ -151,5 +155,6 @@ def get_port_config() -> Optional[dict]:
with open(path, 'r') as f:
return json.load(f)
except Exception as e:
logger.warning(f"Could not read port configuration {path}: {e}")
return None
logger.warning(
f"Could not read port configuration {path}: {e}")
return None
2 changes: 1 addition & 1 deletion UnityMcpBridge/UnityMcpServer~/src/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "4.0.0"
description = "MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP)."
readme = "README.md"
requires-python = ">=3.10"
dependencies = ["httpx>=0.27.2", "mcp[cli]>=1.4.1"]
dependencies = ["httpx>=0.27.2", "mcp[cli]>=1.15.0"]

[build-system]
requires = ["setuptools>=64.0.0", "wheel"]
Expand Down
1 change: 1 addition & 0 deletions UnityMcpBridge/UnityMcpServer~/src/reload_sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
All functions are no-ops to prevent accidental external writes.
"""


def flip_reload_sentinel(*args, **kwargs) -> str:
return "reload_sentinel.py is deprecated; use execute_menu_item → 'MCP/Flip Reload Sentinel'"
8 changes: 3 additions & 5 deletions UnityMcpBridge/UnityMcpServer~/src/server.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from mcp.server.fastmcp import FastMCP, Context, Image
from mcp.server.fastmcp import FastMCP
import logging
from logging.handlers import RotatingFileHandler
import os
from dataclasses import dataclass
from contextlib import asynccontextmanager
from typing import AsyncIterator, Dict, Any, List
from typing import AsyncIterator, Dict, Any
from config import config
from tools import register_all_tools
from unity_connection import get_unity_connection, UnityConnection
Expand Down Expand Up @@ -150,8 +149,7 @@ def _emit_startup():

# Initialize MCP server
mcp = FastMCP(
"mcp-for-unity-server",
description="Unity Editor integration via Model Context Protocol",
name="mcp-for-unity-server",
lifespan=server_lifespan
)

Expand Down
Loading