Standardize how we define MCP tools#292
Conversation
WalkthroughReplaces the editor's manual ExecuteCommand switch with registry-based dispatch; adds Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Bridge as Unity Editor Bridge
participant Registry as CommandRegistry
participant Handler as Command Handler
Client->>Bridge: ExecuteCommand(command)
Bridge->>Registry: GetHandler(command.type)
alt handler found
Registry-->>Bridge: handlerFunc
Bridge->>Handler: Invoke(paramsObject)
Handler-->>Bridge: JSON response
Bridge-->>Client: Response JSON
else missing / error
Registry-->>Bridge: None / throws
Bridge-->>Client: Error JSON
end
sequenceDiagram
autonumber
actor MCPClient
participant Tool as MCP Tool
participant Conn as UnityConnection
participant Unity as Unity Editor
MCPClient->>Tool: call(tool, annotated params)
Tool->>Tool: normalize & filter params
Tool->>Conn: send_command_with_retry(type, params)
Conn->>Unity: framed request
Unity-->>Conn: framed response
Conn-->>Tool: parsed result
Tool-->>MCPClient: dict result (success/error)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| $"Unknown or unsupported command type: {command.type}" | ||
| ), | ||
| }; | ||
| object result = CommandRegistry.GetHandler(command.type)(paramsObject); |
There was a problem hiding this comment.
This was removed in an earlier PR, it probably re-appeared by a bad merge conflict resolution
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
UnityMcpBridge/UnityMcpServer~/src/port_discovery.py (1)
65-69: Fix probe payload to match Unity bridge protocolThe Unity bridge speaks newline-delimited JSON (
UnityConnectionalways writesjson.dumps(...)+b"\n"). Sending the bare bytesb"ping"never produces a response—the C# side keeps waiting for a newline—so_try_probe_unity_mcpalways returnsFalse. That forces discovery to fall back to the “first seen” port, which can be stale or belong to another project, defeating the resilience we’re trying to add. Please emit the same framed JSON ping we use elsewhere.- s.sendall(b"ping") + s.sendall(b'{"command":"ping"}\n')UnityMcpBridge/UnityMcpServer~/src/tools/read_console.py (1)
47-55: Restore Unity connection handling or drop it
get_unity_connection()is still invoked here, but the import was removed. As soon as the tool runs, this will raise aNameErrorand the call never reachessend_command_with_retry. Either re-import the symbol or, since the retry helper already manages the connection, just delete this block. The latter keeps the code lean and avoids the unused variable too.- # Get the connection instance - bridge = get_unity_connection()
🧹 Nitpick comments (11)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (2)
17-39: Avoid per-call inspect.signature; precompute action param once.This decorates hot paths; binding the signature on every call is unnecessary. Cache the action parameter position at decoration time and read from args/kwargs.
Apply:
def telemetry_tool(tool_name: str): """Decorator to add telemetry tracking to MCP tools""" def decorator(func: Callable) -> Callable: + # Precompute 'action' param position to avoid per-call introspection + try: + _sig = inspect.signature(func) + _param_names = tuple(_sig.parameters.keys()) + _action_idx = _param_names.index("action") if "action" in _param_names else None + except Exception: + _action_idx = None + + def _extract_sub_action(args, kwargs): + if "action" in kwargs: + return kwargs.get("action") + if _action_idx is not None and len(args) > _action_idx: + return args[_action_idx] + return None @@ - def _sync_wrapper(*args, **kwargs) -> Any: + def _sync_wrapper(*args, **kwargs) -> Any: start_time = time.time() success = False error = None # Extract sub-action (e.g., 'get_hierarchy') from bound args when available - sub_action = None - try: - sig = inspect.signature(func) - bound = sig.bind_partial(*args, **kwargs) - bound.apply_defaults() - sub_action = bound.arguments.get("action") - except Exception: - sub_action = None + sub_action = _extract_sub_action(args, kwargs) @@ - async def _async_wrapper(*args, **kwargs) -> Any: + async def _async_wrapper(*args, **kwargs) -> Any: start_time = time.time() success = False error = None # Extract sub-action (e.g., 'get_hierarchy') from bound args when available - sub_action = None - try: - sig = inspect.signature(func) - bound = sig.bind_partial(*args, **kwargs) - bound.apply_defaults() - sub_action = bound.arguments.get("action") - except Exception: - sub_action = None + sub_action = _extract_sub_action(args, kwargs)Also applies to: 63-83
46-51: Minor: tighten milestone scene check or document prefix contract.Using startswith("manage_scene") is fine; if tool names change (e.g., "scene"), milestones could drift. Optionally constrain to exact name or a documented prefix convention.
Also applies to: 89-94
UnityMcpBridge/Editor/MCPForUnityBridge.cs (1)
1071-1086: Remove now-unused special-case manage_scene handler.
HandleManageSceneappears unused after registry dispatch. Consider removing to reduce dead code and stale behavior drift.UnityMcpBridge/UnityMcpServer~/src/tools/manage_prefabs.py (3)
16-22: Restore explicit enum description for action.“Operation” is less helpful to clients than listing allowed values. Include them in the annotation text to align with FastMCP’s parameter metadata guidance.
Apply:
- ], "Operation"], + ], "Operation: one of open_stage | close_stage | save_open_stage | create_from_gameobject"],
24-34: Optional: restrict mode via Literal for clearer contracts.If only InIsolation is supported, encode it in the type to help LLMs and clients.
Example:
- mode: Annotated[str | None, - "Optional prefab stage mode (only 'InIsolation' is currently supported)"] = None, + mode: Annotated[Literal["InIsolation"] | None, + "Prefab stage mode (currently only 'InIsolation')"] = None,
35-61: Consider lightweight precondition checks for action-specific args.E.g., require target and prefab_path for create_from_gameobject; return a friendly error before bridging. Keeps Unity logs cleaner without adopting full Pydantic.
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (4)
359-369: Add Annotated parameter descriptions to align with FastMCP metadata.This tool lacks Annotated[...] metadata for params; adding them improves discoverability and standardization.
Apply minimal updates:
-from typing import Dict, Any, List, Tuple, Optional +from typing import Dict, Any, List, Tuple, Optional, Annotated @@ - def script_apply_edits( - ctx: Context, - name: str, - path: str, - edits: List[Dict[str, Any]], - options: Optional[Dict[str, Any]] = None, - script_type: str = "MonoBehaviour", - namespace: str = "", - ) -> Dict[str, Any]: + def script_apply_edits( + ctx: Context, + name: Annotated[str, "Script name (with or without .cs)"], + path: Annotated[str, "Directory under Assets or full Assets/.../File.cs"], + edits: Annotated[List[Dict[str, Any]], "Structured/text edits to apply"], + options: Annotated[Optional[Dict[str, Any]], "Options: validate, refresh, preview, confirm"] = None, + script_type: Annotated[str, "Unity script type (e.g., MonoBehaviour)"] = "MonoBehaviour", + namespace: Annotated[str, "Namespace (optional)"] = "", + ) -> Dict[str, Any]:
669-677: Broad excepts are acceptable here; consider narrowing where feasible.Error mapping is user-facing. If practical, catch regex compilation errors explicitly (ValueError/re.error) to improve diagnostics and keep BLE001 quiet.
Also applies to: 822-835
871-888: Guard oversized diffs for preview to keep responses small.You already truncate; consider emitting total-changes metadata to help clients decide to fetch more.
883-890: Consistency nit: return shapes.Some error returns use {"success": False, "message": ...}, others include "code". Consider adding code consistently for easier client handling.
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py (1)
101-103: Remove the unusedconnectionassignmentCalling
get_unity_connection()purely for its side effect is fine, but assigning it toconnectionand never using it trips lint and adds noise. Dropping the assignment (or reusing the object) keeps the intent clear.- connection = get_unity_connection() + get_unity_connection()
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
UnityMcpBridge/Editor/MCPForUnityBridge.cs(1 hunks)UnityMcpBridge/Editor/Tools/CommandRegistry.cs(2 hunks)UnityMcpBridge/Editor/Tools/ManageScript.cs(9 hunks)UnityMcpBridge/UnityMcpServer~/src/Dockerfile(1 hunks)UnityMcpBridge/UnityMcpServer~/src/__init__.py(1 hunks)UnityMcpBridge/UnityMcpServer~/src/config.py(2 hunks)UnityMcpBridge/UnityMcpServer~/src/port_discovery.py(6 hunks)UnityMcpBridge/UnityMcpServer~/src/pyproject.toml(1 hunks)UnityMcpBridge/UnityMcpServer~/src/reload_sentinel.py(1 hunks)UnityMcpBridge/UnityMcpServer~/src/server.py(2 hunks)UnityMcpBridge/UnityMcpServer~/src/telemetry.py(17 hunks)UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py(5 hunks)UnityMcpBridge/UnityMcpServer~/src/test_telemetry.py(4 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/__init__.py(2 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py(3 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py(3 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_gameobject.py(4 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_menu_item.py(1 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_prefabs.py(2 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py(3 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py(21 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py(26 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_shader.py(1 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/read_console.py(5 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py(9 hunks)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py(18 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-04T01:01:11.927Z
Learnt from: dsarno
PR: CoplayDev/unity-mcp#260
File: UnityMcpBridge/UnityMcpServer~/src/server_version.txt:1-1
Timestamp: 2025-09-04T01:01:11.927Z
Learning: The UnityMcpBridge project is not maintaining changelogs yet, so don't suggest adding changelog entries for version bumps.
Applied to files:
UnityMcpBridge/UnityMcpServer~/src/DockerfileUnityMcpBridge/UnityMcpServer~/src/pyproject.tomlUnityMcpBridge/UnityMcpServer~/src/__init__.py
🧬 Code graph analysis (15)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
UnityMcpBridge/UnityMcpServer~/src/telemetry.py (4)
record_tool_usage(403-429)record_milestone(249-271)record_milestone(398-400)MilestoneType(49-57)
UnityMcpBridge/Editor/MCPForUnityBridge.cs (1)
UnityMcpBridge/Editor/Tools/CommandRegistry.cs (1)
CommandRegistry(12-49)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/Editor/Tools/CommandRegistry.cs (1)
UnityMcpBridge/Editor/Tools/Prefabs/ManagePrefabs.cs (1)
ManagePrefabs(12-273)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_prefabs.py (1)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py (3)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/telemetry.py (2)
is_telemetry_enabled(458-460)record_tool_usage(403-429)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py (3)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (2)
get_unity_connection(378-399)async_send_command_with_retry(439-452)UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)tests/test_manage_script_uri.py (1)
tool(46-50)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_shader.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (2)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (2)
telemetry_tool(17-107)decorator(19-106)
UnityMcpBridge/UnityMcpServer~/src/test_telemetry.py (1)
UnityMcpBridge/UnityMcpServer~/src/telemetry.py (7)
get_telemetry(383-388)record_telemetry(391-395)record_milestone(249-271)record_milestone(398-400)RecordType(37-46)MilestoneType(49-57)is_telemetry_enabled(458-460)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_gameobject.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/UnityMcpServer~/src/tools/read_console.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/Editor/Tools/ManageScript.cs (1)
UnityMcpBridge/Editor/Helpers/Response.cs (3)
Response(10-61)Error(41-60)Success(18-33)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
UnityMcpBridge/UnityMcpServer~/src/port_discovery.py (1)
PortDiscovery(24-160)
UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py (3)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py (1)
_coerce_int(62-75)
🪛 Ruff (0.13.1)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py
103-103: Do not catch blind exception: Exception
(BLE001)
UnityMcpBridge/UnityMcpServer~/src/port_discovery.py
127-127: Do not catch blind exception: Exception
(BLE001)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
67-67: Avoid specifying long messages outside the exception class
(TRY003)
88-89: Avoid specifying long messages outside the exception class
(TRY003)
681-681: Do not catch blind exception: Exception
(BLE001)
833-833: Do not catch blind exception: Exception
(BLE001)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py
15-15: Unused function argument: ctx
(ARG001)
58-58: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py
16-16: Unused function argument: ctx
(ARG001)
17-18: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
19-19: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
22-22: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
23-23: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
24-24: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
57-57: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py
19-19: Unused function argument: ctx
(ARG001)
101-101: Local variable connection is assigned to but never used
Remove assignment to unused variable connection
(F841)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_shader.py
16-16: Unused function argument: ctx
(ARG001)
60-60: Do not catch blind exception: Exception
(BLE001)
62-62: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py
16-16: Unused function argument: tool_name
(ARG001)
92-92: Unused function argument: ctx
(ARG001)
169-170: try-except-pass detected, consider logging the exception
(S110)
169-169: Do not catch blind exception: Exception
(BLE001)
234-235: try-except-pass detected, consider logging the exception
(S110)
234-234: Do not catch blind exception: Exception
(BLE001)
370-370: Unused function argument: ctx
(ARG001)
407-407: Unused function argument: ctx
(ARG001)
421-421: Unused function argument: ctx
(ARG001)
454-454: Unused function argument: ctx
(ARG001)
514-515: try-except-pass detected, consider logging the exception
(S110)
514-514: Do not catch blind exception: Exception
(BLE001)
577-577: Unused function argument: ctx
(ARG001)
602-602: Unused function argument: ctx
(ARG001)
UnityMcpBridge/UnityMcpServer~/src/test_telemetry.py
25-25: Local variable e is assigned to but never used
Remove assignment to unused variable e
(F841)
57-57: Do not catch blind exception: Exception
(BLE001)
57-57: Local variable e is assigned to but never used
Remove assignment to unused variable e
(F841)
117-117: Consider moving this statement to an else block
(TRY300)
119-119: Do not catch blind exception: Exception
(BLE001)
119-119: Local variable e is assigned to but never used
Remove assignment to unused variable e
(F841)
UnityMcpBridge/UnityMcpServer~/src/telemetry.py
124-124: Do not catch blind exception: Exception
(BLE001)
128-129: try-except-pass detected, consider logging the exception
(S110)
128-128: Do not catch blind exception: Exception
(BLE001)
179-180: Abstract raise to an inner function
(TRY301)
179-180: Avoid specifying long messages outside the exception class
(TRY003)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_gameobject.py
15-15: Unused function argument: ctx
(ARG001)
17-17: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
19-19: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
21-21: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
23-23: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
25-25: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
27-27: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
29-29: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
31-31: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
33-33: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
35-35: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
39-39: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
43-43: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
45-45: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
46-46: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
48-48: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
55-55: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
63-63: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
66-66: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
126-126: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/read_console.py
18-18: Unused function argument: ctx
(ARG001)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
94-95: Avoid specifying long messages outside the exception class
(TRY003)
128-129: Avoid specifying long messages outside the exception class
(TRY003)
154-155: Abstract raise to an inner function
(TRY301)
154-155: Avoid specifying long messages outside the exception class
(TRY003)
174-175: Abstract raise to an inner function
(TRY301)
174-175: Create your own exception
(TRY002)
174-175: Avoid specifying long messages outside the exception class
(TRY003)
208-208: Consider moving this statement to an else block
(TRY300)
212-212: Do not catch blind exception: Exception
(BLE001)
214-214: Use explicit conversion flag
Replace with conversion flag
(RUF010)
320-320: Abstract raise to an inner function
(TRY301)
320-320: Create your own exception
(TRY002)
396-397: Avoid specifying long messages outside the exception class
(TRY003)
UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
142-142: Unused function argument: ctx
(ARG001)
194-194: Unused function argument: ctx
(ARG001)
353-353: Unused function argument: ctx
(ARG001)
🔇 Additional comments (2)
UnityMcpBridge/Editor/MCPForUnityBridge.cs (1)
1042-1046: Registry dispatch verified
All expected handlers, includingmanage_prefabs, are registered in CommandRegistry.UnityMcpBridge/UnityMcpServer~/src/tools/__init__.py (1)
18-33: register_all_tools invocation and metadata usage verified: function is only called once and all @mcp.tool definitions include descriptions and Annotated parameters.
| if isinstance(response, dict) and response.get("success"): | ||
| # If the response contains base64 encoded content, decode it | ||
| if response.get("data", {}).get("contentsEncoded"): | ||
| decoded_contents = base64.b64decode(response["data"]["encodedContents"]).decode('utf-8') | ||
| decoded_contents = base64.b64decode( | ||
| response["data"]["encodedContents"]).decode('utf-8') | ||
| response["data"]["contents"] = decoded_contents | ||
| del response["data"]["encodedContents"] | ||
| del response["data"]["contentsEncoded"] | ||
|
|
||
| return {"success": True, "message": response.get("message", "Operation successful."), "data": response.get("data")} |
There was a problem hiding this comment.
Normalize data before probing for encoded shader contents
When Unity returns a success payload whose data field is either null or a non-dict (several tools already send strings here), calling .get(...) on it raises an AttributeError. That propagates into the catch block, so the caller only sees "Python error managing shader: '...' " instead of the original Unity response. Guard the branch to run only when data is actually a mapping, and fall back gracefully otherwise.
- if response.get("data", {}).get("contentsEncoded"):
- decoded_contents = base64.b64decode(
- response["data"]["encodedContents"]).decode('utf-8')
- response["data"]["contents"] = decoded_contents
- del response["data"]["encodedContents"]
- del response["data"]["contentsEncoded"]
+ data = response.get("data")
+ if isinstance(data, dict) and data.get("contentsEncoded"):
+ encoded = data.get("encodedContents")
+ if encoded:
+ decoded_contents = base64.b64decode(encoded).decode('utf-8')
+ data = {**data, "contents": decoded_contents}
+ data.pop("encodedContents", None)
+ data.pop("contentsEncoded", None)
+ response["data"] = data📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if isinstance(response, dict) and response.get("success"): | |
| # If the response contains base64 encoded content, decode it | |
| if response.get("data", {}).get("contentsEncoded"): | |
| decoded_contents = base64.b64decode(response["data"]["encodedContents"]).decode('utf-8') | |
| decoded_contents = base64.b64decode( | |
| response["data"]["encodedContents"]).decode('utf-8') | |
| response["data"]["contents"] = decoded_contents | |
| del response["data"]["encodedContents"] | |
| del response["data"]["contentsEncoded"] | |
| return {"success": True, "message": response.get("message", "Operation successful."), "data": response.get("data")} | |
| if isinstance(response, dict) and response.get("success"): | |
| # If the response contains base64 encoded content, decode it | |
| data = response.get("data") | |
| if isinstance(data, dict) and data.get("contentsEncoded"): | |
| encoded = data.get("encodedContents") | |
| if encoded: | |
| decoded_contents = base64.b64decode(encoded).decode('utf-8') | |
| data = {**data, "contents": decoded_contents} | |
| data.pop("encodedContents", None) | |
| data.pop("contentsEncoded", None) | |
| response["data"] = data | |
| return {"success": True, "message": response.get("message", "Operation successful."), "data": response.get("data")} |
There was a problem hiding this comment.
Ignoring, I only made a formatting change, the logic can be revisited later
|
Will test, respond to feedback and release tomorrow |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py (1)
174-181: Let callers list non-script assets.Hard-coding the
.cssuffix here defeats thepatternparameter—pattern="*.shader"(or any non-.csglob) will always return an empty list even though the annotation and description promise configurability. That regresses existing capability to browse other asset types. Please drop the forced suffix check and let the glob drive the filter, defaulting to*.csonly when the caller doesn’t supply one.- # Enforce .cs extension regardless of provided pattern - if p.suffix.lower() != ".cs": - continue - if pattern and not fnmatch.fnmatch(p.name, pattern): + glob = pattern or "*.cs" + if not fnmatch.fnmatch(p.name, glob): continueUnityMcpBridge/UnityMcpServer~/src/tools/read_console.py (1)
47-55: Restore theget_unity_connectionimport or drop the unused call.
get_unity_connectionis no longer imported, yet Line 48 still calls it. This will raiseNameErrorat runtime before we ever build params. Either re-importget_unity_connectionor, since the result is unused andsend_command_with_retryalready acquires the connection internally, just remove the call entirely. I recommend the latter to avoid redundant work.- # Get the connection instance - bridge = get_unity_connection() - # Set defaults if values are None action = action if action is not None else 'get' types = types if types is not None else ['error', 'warning', 'log']
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py(3 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py(3 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_gameobject.py(4 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_menu_item.py(1 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_prefabs.py(2 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py(3 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py(21 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py(27 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_shader.py(1 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/read_console.py(5 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py(9 hunks)
🧰 Additional context used
🧬 Code graph analysis (10)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py (2)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (2)
get_unity_connection(378-399)async_send_command_with_retry(439-452)UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py (3)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/telemetry.py (2)
is_telemetry_enabled(458-460)record_tool_usage(403-429)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_gameobject.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_menu_item.py (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)
telemetry_tool(16-19)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_shader.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (3)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (2)
telemetry_tool(17-107)decorator(19-106)tests/test_script_tools.py (2)
decorator(44-46)tool(43-47)
UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py (3)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py (1)
_coerce_int(62-75)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_prefabs.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
🪛 Ruff (0.13.1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py
19-19: Unused function argument: ctx
(ARG001)
101-101: Local variable connection is assigned to but never used
Remove assignment to unused variable connection
(F841)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py
16-16: Unused function argument: ctx
(ARG001)
17-18: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
19-19: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
22-22: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
23-23: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
24-24: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
57-57: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_gameobject.py
15-15: Unused function argument: ctx
(ARG001)
17-17: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
19-19: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
21-21: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
23-23: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
25-25: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
27-27: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
29-29: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
31-31: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
33-33: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
35-35: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
39-39: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
43-43: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
45-45: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
46-46: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
48-48: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
55-55: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
63-63: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
66-66: PEP 484 prohibits implicit Optional
Convert to Optional[T]
(RUF013)
126-126: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py
15-15: Unused function argument: ctx
(ARG001)
58-58: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_menu_item.py
19-19: Unused function argument: ctx
(ARG001)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_shader.py
16-16: Unused function argument: ctx
(ARG001)
60-60: Do not catch blind exception: Exception
(BLE001)
62-62: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
67-67: Avoid specifying long messages outside the exception class
(TRY003)
88-89: Avoid specifying long messages outside the exception class
(TRY003)
681-681: Do not catch blind exception: Exception
(BLE001)
833-833: Do not catch blind exception: Exception
(BLE001)
UnityMcpBridge/UnityMcpServer~/src/tools/read_console.py
18-18: Unused function argument: ctx
(ARG001)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py
16-16: Unused function argument: tool_name
(ARG001)
92-92: Unused function argument: ctx
(ARG001)
169-170: try-except-pass detected, consider logging the exception
(S110)
169-169: Do not catch blind exception: Exception
(BLE001)
234-235: try-except-pass detected, consider logging the exception
(S110)
234-234: Do not catch blind exception: Exception
(BLE001)
370-370: Unused function argument: ctx
(ARG001)
407-407: Unused function argument: ctx
(ARG001)
421-421: Unused function argument: ctx
(ARG001)
454-454: Unused function argument: ctx
(ARG001)
514-515: try-except-pass detected, consider logging the exception
(S110)
514-514: Do not catch blind exception: Exception
(BLE001)
577-577: Unused function argument: ctx
(ARG001)
602-602: Unused function argument: ctx
(ARG001)
UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
142-142: Unused function argument: ctx
(ARG001)
194-194: Unused function argument: ctx
(ARG001)
353-353: Unused function argument: ctx
(ARG001)
🔇 Additional comments (5)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)
455-463: Re-include'update'in the advertised action Literal.With
'update'removed from theLiteral, FastMCP will now reject legacymanage_scriptupdate calls before they can reach the in-function migration path, breaking the compatibility you’re preserving. Please add'update'back (mark it deprecated in the description if desired) so existing clients keep working.- action: Annotated[Literal['create', 'read', 'delete'], "Operations"], + action: Annotated[ + Literal['create', 'read', 'update', 'delete'], + "Operations (legacy 'update' is deprecated—prefer apply_text_edits)" + ],UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (2)
606-611: Fix encoded payload fallback.Line 610 still requires both
contentsEncodedandencodedContents, so payloads that only send the encoded string keep returning “No contents returned from Unity read.” Please decode whenever either encoded field is present (preferencodedContents), guarding for non-string values.- contents = data.get("contents") - if contents is None and data.get("contentsEncoded") and data.get("encodedContents"): - contents = base64.b64decode( - data["encodedContents"]).decode("utf-8") + contents = data.get("contents") + if contents is None: + encoded_val = data.get("encodedContents") + if isinstance(encoded_val, str): + contents = base64.b64decode(encoded_val).decode("utf-8") + elif isinstance(data.get("contentsEncoded"), str): + contents = base64.b64decode( + data["contentsEncoded"]).decode("utf-8")
761-763: Removeanchor_insertfromstructured_kinds.Line 762 keeps
anchor_insertin the structured set, so the text-conversion branch that advertises anchor support never runs. Dropping it here lets pure anchor inserts reach the text-edits path as intended.- structured_kinds = {"replace_class", "delete_class", - "replace_method", "delete_method", "insert_method", "anchor_insert"} + structured_kinds = {"replace_class", "delete_class", + "replace_method", "delete_method", "insert_method"}UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py (1)
17-20: Revertname/pathto optional parameters.Requiring
nameandpathmakes previously valid calls likemanage_scene(action="get_hierarchy")raiseTypeErrorbefore our logic runs, breaking existing clients and telemetry (see earlier review). These values still behave as optional inside the function, so restore their defaults.- name: Annotated[str, "Scene name (no extension) for create/load/save"], - path: Annotated[str, "Asset path for scene operations (default: 'Assets/')"], + name: Annotated[str | None, "Scene name (no extension) for create/load/save"] = None, + path: Annotated[str | None, "Asset path for scene operations (default: 'Assets/')"] = None,UnityMcpBridge/UnityMcpServer~/src/tools/manage_shader.py (1)
48-55: Guardresponse["data"]before decoding shader payloads.If Unity replies with
{success: True, data: None}(or any non-dict),response.get("data", {}).get("contentsEncoded")raisesAttributeError, which dumps us into the catch block and hides the real Unity response—exactly the failure called out in the earlier review. Please normalizedatato a dict before touching it so we don't crash on valid responses.- if response.get("data", {}).get("contentsEncoded"): - decoded_contents = base64.b64decode( - response["data"]["encodedContents"]).decode('utf-8') - response["data"]["contents"] = decoded_contents - del response["data"]["encodedContents"] - del response["data"]["contentsEncoded"] + data = response.get("data") + if isinstance(data, dict) and data.get("contentsEncoded"): + encoded = data.get("encodedContents") + if encoded: + decoded_contents = base64.b64decode(encoded).decode('utf-8') + data["contents"] = decoded_contents + data.pop("encodedContents", None) + data.pop("contentsEncoded", None) + response["data"] = data
| { "read_console", ReadConsole.HandleCommand }, | ||
| { "manage_menu_item", ManageMenuItem.HandleCommand }, | ||
| { "manage_shader", ManageShader.HandleCommand}, | ||
| { "manage_prefabs", ManagePrefabs.HandleCommand}, |
There was a problem hiding this comment.
Issue with earlier merge, this should have been in
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py (1)
54-57: Preserve full Unity payload on successWhen Unity responds with
success: True, we now drop every other key exceptmessageanddata. Existing actions likeget_hierarchyandget_build_settingsreturn structured payloads (e.g.,hierarchy,scenes) outside ofdata, and they lose that information after this change. Please keep returning the original dict (or merge in the friendly message without discarding other fields) so downstream clients continue to receive the full payload.Apply this diff to retain the original response while still exposing the friendly message:
- if isinstance(response, dict) and response.get("success"): - return {"success": True, "message": response.get("message", "Scene operation successful."), "data": response.get("data")} - return response if isinstance(response, dict) else {"success": False, "message": str(response)} + if isinstance(response, dict): + if response.get("success"): + response.setdefault("message", "Scene operation successful.") + return response + return {"success": False, "message": str(response)}
🧹 Nitpick comments (4)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py (4)
17-26: Optional parameter typing is correct; consider polishing the action’s description.
- Using Annotated[T] | None for optionals resolves implicit Optional (RUF013) and ensures correct FastMCP schema. Good.
- Minor: “Operations” is vague; a clearer description (e.g., “Operation to perform”) helps clients render better tooltips.
Suggested change:
- "get_active_tool", "get_selection", "get_prefab_stage", "set_active_tool", "add_tag", "remove_tag", "get_tags", "add_layer", "remove_layer", "get_layers"], "Operations"], + "get_active_tool", "get_selection", "get_prefab_stage", "set_active_tool", "add_tag", "remove_tag", "get_tags", "add_layer", "remove_layer", "get_layers"], "Operation to perform"],
36-47: Pre‑validate companion params for clearer client errors.Returning early on missing companion args gives immediate, actionable feedback instead of a round‑trip error from Unity.
if action == "telemetry_ping": record_tool_usage("diagnostic_ping", True, 1.0, None) return {"success": True, "message": "telemetry ping queued"} - # Prepare parameters, removing None values + # Validate required companion parameters for certain actions + if action == "set_active_tool" and not tool_name: + return {"success": False, "message": "tool_name is required when action is 'set_active_tool'."} + if action in {"add_tag", "remove_tag"} and not tag_name: + return {"success": False, "message": "tag_name is required when action is 'add_tag' or 'remove_tag'."} + if action in {"add_layer", "remove_layer"} and not layer_name: + return {"success": False, "message": "layer_name is required when action is 'add_layer' or 'remove_layer'."} + # Prepare parameters, removing None values
16-16: Rename unused ctx param or suppress to satisfy Ruff ARG001.Change the param name to underscore‑prefixed:
- ctx: Context, + _ctx: Context,Alternatively, keep the name and add a no‑op reference before use (less clean):
_ = ctx.Based on static analysis hints
59-59: Use f‑string conversion flag to satisfy RUF010.- return {"success": False, "message": f"Python error managing editor: {str(e)}"} + return {"success": False, "message": f"Python error managing editor: {e!s}"}Based on static analysis hints
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py(3 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py(3 hunks)UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py (4)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (2)
get_unity_connection(378-399)async_send_command_with_retry(439-452)UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)tests/test_manage_script_uri.py (1)
tool(46-50)tests/test_script_tools.py (1)
tool(43-47)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (1)
telemetry_tool(17-107)UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)
🪛 Ruff (0.13.1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py
19-19: Unused function argument: ctx
(ARG001)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py
16-16: Unused function argument: ctx
(ARG001)
59-59: Use explicit conversion flag
Replace with conversion flag
(RUF010)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py
15-15: Unused function argument: ctx
(ARG001)
60-60: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🔇 Additional comments (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_editor.py (1)
1-1: Adopts FastMCP conventions and centralized retry — looks good.
- Typed imports, named tool decorator with description, camelCase payload keys (toolName/layerName), and None‑filtering all align with FastMCP + Unity payload expectations.
Also applies to: 3-3, 7-7, 13-13, 40-42, 49-49
|
Lots of good feedback, and tested a good bit. Merging in |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)
152-173: Zero-based explicit coords are clamped, not translated (+1); edits land at wrong positions.When explicit fields are zero-based, the code sets all <1 values to 1 instead of translating to 1-based. This corrupts target ranges. Translate all four coords by +1 (then clamp) in both branches.
Apply this diff in both blocks:
- for k in ("startLine", "startCol", "endLine", "endCol"): - try: - if int(e2.get(k, 1)) < 1: - e2[k] = 1 - except Exception: - pass + for k in ("startLine", "startCol", "endLine", "endCol"): + try: + v = int(e2.get(k, 0)) + e2[k] = max(1, v + 1) + except Exception: + passAlso consider logging instead of bare
except ...: passto aid debugging.Also applies to: 217-239
🧹 Nitpick comments (2)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (2)
459-465: Consolidate Annotated metadata strings per param (cleaner for FastMCP parsers).Multiple string metadata entries may be ignored by some clients. Prefer a single descriptive string.
- name: Annotated[str, "Script name (no .cs extension)", "Name of the script to create"], - path: Annotated[str, "Asset path (default: 'Assets/')", "Path under Assets/ to create the script at, e.g., 'Assets/Scripts/My.cs'"], - contents: Annotated[str, "Contents of the script to create", - "C# code for 'create'/'update'"] | None = None, - script_type: Annotated[str, "Script type (e.g., 'C#')", - "Type hint (e.g., 'MonoBehaviour')"] | None = None, + name: Annotated[str, "Script name without .cs; for create, becomes the new file's base name."], + path: Annotated[str, "Assets-relative path. Example: Assets/Scripts/My.cs"], + contents: Annotated[str, "C# contents for create/update (plain text; encoded for transport internally)."] | None = None, + script_type: Annotated[str, "Script type (e.g., 'C#') or hint (e.g., 'MonoBehaviour')."] | None = None,
294-307: Remove unused difflib import or return a minimal diff preview.
diffliband locals are unused; keep it lean or compute a small unified diff if needed.- import difflib - # Apply locally to preview final result - lines = [] - # Build an indexable original from a read if we normalized from read; otherwise skip - prev = "" - # We cannot guarantee file contents here without a read; return normalized spans only + # Return normalized spans only (no local diff build)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py(18 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (2)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
send_command_with_retry(416-436)UnityMcpBridge/UnityMcpServer~/src/telemetry_decorator.py (2)
telemetry_tool(17-107)decorator(19-106)
🪛 Ruff (0.13.1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py
16-16: Unused function argument: tool_name
(ARG001)
169-170: try-except-pass detected, consider logging the exception
(S110)
169-169: Do not catch blind exception: Exception
(BLE001)
234-235: try-except-pass detected, consider logging the exception
(S110)
234-234: Do not catch blind exception: Exception
(BLE001)
🔇 Additional comments (5)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (5)
76-89: Good FastMCP metadata and UX notes for apply_text_edits.Clear description, step-by-step guidance, and 1-indexing callout are on point.
380-390: Path hygiene looks solid.Good normalization and guardrails against traversal/absolute paths; ensures under Assets/.
515-544: Capability surface is concise and consistent.Clear ops/text_ops and max payload; extras advertise get_sha for clients. LGTM.
547-566: Minimal SHA endpoint is useful and safe.Returns only sha256/lengthBytes; good for optimistic concurrency without content fetch.
454-466: Restore 'update' in the action Literal to keep legacy calls working.Function still routes legacy update flows, but the enum excludes it, so FastMCP clients will reject those requests pre-dispatch.
- action: Annotated[Literal['create', 'read', 'delete'], "Perform CRUD operations on C# scripts."], + action: Annotated[Literal['create', 'read', 'update', 'delete'], "Perform CRUD operations on C# scripts. Note: 'update' is deprecated but retained for backward compatibility."],#!/bin/bash # Find any in-repo callers still sending 'update' rg -nP -C2 "['\"]action['\"]\s*:\s*['\"]update['\"]"
* refactor: migrate command routing to use CommandRegistry lookup instead of switch statement * style: improve code formatting and indentation consistency * refactor: clean up imports and type hints across tool modules * Revert "feat: Implement Asset Store Compliance for Unity MCP Bridge" This reverts commit 2fca7fc. * Revert "feat(asset-store): implement post-installation prompt system for Asset Store compliance" This reverts commit ab25a71. * chore: upgrade mcp[cli] dependency from 1.4.1 to 1.15.0 * style: fix formatting and whitespace in Python server files * Remove description, probably a Python versionn change * feat: add type hints and parameter descriptions to Unity MCP tools * docs: improve shader management tool parameter descriptions and types * refactor: add type annotations and improve documentation for script management tools * refactor: improve type annotations and documentation in manage_scene tool * refactor: add type annotations and improve parameter descriptions across MCP tools * feat: add explicit name parameters to all MCP tool decorators * refactor: remove unused Unity connection instance in manage_asset_tools * chore: update type hints in manage_editor function parameters for better clarity * feat: make name and path parameters optional for scene management operations * refactor: remove unused get_unity_connection import from manage_asset.py * chore: rename Operation parameter annotation to Operations for consistency * feat: add logging to MCP clients for tool actions across MCP server components * chore: add FastMCP type hint to register_all_tools parameter * style: reformat docstring in apply_text_edits tool to use multiline string syntax * refactor: update type hints from Dict/List/Tuple/Optional to modern Python syntax * refactor: clean up imports and add type annotations to script editing tools * refactor: update type hints to use | None syntax for optional parameters * Minor fixes * docs: improve tool descriptions with clearer action explanations * refactor: remove legacy update action migration code from manage_script.py * style: replace em dashes with regular hyphens in tool descriptions [skip ci] * refactor: convert manage_script_capabilities docstring to multiline format [skip ci]
We're just following FastMCP's docs, minus using Pydantic to validate data entry. Closes #289
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Chores