Skip to content

Commit 18e7a32

Browse files
whatevertogoclaude
andauthored
Fix execute_custom_tool bypassed when unity_instance is specified (#724)
* Fix execute_custom_tool bypassed when unity_instance is specified Move execute_custom_tool handling outside the if/else block so it's executed regardless of whether unity_instance is provided. Previously, when unity_instance was specified, the code would take the if branch (match by hash/name) and return 404 if not found, but the execute_custom_tool block was only in the else branch, causing custom tools to fall through to PluginHub.send_command. Fixes #649 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Handle empty sessions gracefully when selecting default session When no active sessions exist, next(iter(sessions.sessions.keys())) raises StopIteration. Catch this exception and set session_id and session_details to None, allowing execute_custom_tool guard to work as intended. Suggested by Sourcery review. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Move default session selection into execute_custom_tool block Per Sourcery review: moving the default session selection inside execute_custom_tool ensures the empty sessions guard works correctly. If sessions becomes empty between the initial check and this code, the StopIteration will now properly trigger the session validation guard and return a 503 error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove unintended uv.lock changes from PR * 优化 execute_custom_tool 逻辑以优先使用可用会话 --------- Co-authored-by: whatevertogo <whatevertogo@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 038a39e commit 18e7a32

1 file changed

Lines changed: 73 additions & 53 deletions

File tree

Server/src/main.py

Lines changed: 73 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -394,63 +394,83 @@ async def cli_command_route(request: Request) -> JSONResponse:
394394
session_details = details
395395
break
396396

397-
# If a specific unity_instance was requested but not found, return an error
398-
if not session_id:
397+
# If a specific unity_instance was requested but not found, return an error
398+
# (Check done here so execute_custom_tool can also validate the instance)
399+
if unity_instance and not session_id:
400+
return JSONResponse(
401+
{
402+
"success": False,
403+
"error": f"Unity instance '{unity_instance}' not found",
404+
},
405+
status_code=404,
406+
)
407+
408+
# If no specific unity_instance requested, use first available session
409+
# (Must be done before execute_custom_tool check so all command types benefit)
410+
if not session_id:
411+
try:
412+
session_id = next(iter(sessions.sessions.keys()))
413+
session_details = sessions.sessions.get(session_id)
414+
except StopIteration:
415+
# No sessions available - sessions.sessions is empty
416+
# This should not happen since we checked at line 378, but handle gracefully
417+
return JSONResponse({
418+
"success": False,
419+
"error": "No Unity instances connected. Make sure Unity is running with MCP plugin."
420+
}, status_code=503)
421+
422+
# Custom tool execution - must be checked BEFORE the final PluginHub.send_command call
423+
# This applies to both cases: with or without explicit unity_instance
424+
if command_type == "execute_custom_tool":
425+
# session_id and session_details are already set above
426+
if not session_id or not session_details:
399427
return JSONResponse(
400-
{
401-
"success": False,
402-
"error": f"Unity instance '{unity_instance}' not found",
403-
},
404-
status_code=404,
428+
{"success": False,
429+
"error": "No valid Unity session available for custom tool execution"},
430+
status_code=503,
405431
)
406-
else:
407-
# No specific unity_instance requested: use first available session
408-
session_id = next(iter(sessions.sessions.keys()))
409-
session_details = sessions.sessions.get(session_id)
410-
411-
if command_type == "execute_custom_tool":
412-
tool_name = None
432+
tool_name = None
433+
tool_params = {}
434+
if isinstance(params, dict):
435+
tool_name = params.get(
436+
"tool_name") or params.get("name")
437+
tool_params = params.get(
438+
"parameters") or params.get("params") or {}
439+
440+
if not tool_name:
441+
return JSONResponse(
442+
{"success": False,
443+
"error": "Missing 'tool_name' for execute_custom_tool"},
444+
status_code=400,
445+
)
446+
if tool_params is None:
413447
tool_params = {}
414-
if isinstance(params, dict):
415-
tool_name = params.get(
416-
"tool_name") or params.get("name")
417-
tool_params = params.get(
418-
"parameters") or params.get("params") or {}
419-
420-
if not tool_name:
421-
return JSONResponse(
422-
{"success": False,
423-
"error": "Missing 'tool_name' for execute_custom_tool"},
424-
status_code=400,
425-
)
426-
if tool_params is None:
427-
tool_params = {}
428-
if not isinstance(tool_params, dict):
429-
return JSONResponse(
430-
{"success": False,
431-
"error": "Tool parameters must be an object/dict"},
432-
status_code=400,
433-
)
434-
435-
# Prefer a concrete hash for project-scoped tools.
436-
unity_instance_hint = unity_instance
437-
if session_details and session_details.hash:
438-
unity_instance_hint = session_details.hash
439-
440-
project_id = resolve_project_id_for_unity_instance(
441-
unity_instance_hint)
442-
if not project_id:
443-
return JSONResponse(
444-
{"success": False,
445-
"error": "Could not resolve project id for custom tool"},
446-
status_code=400,
447-
)
448-
449-
service = CustomToolService.get_instance()
450-
result = await service.execute_tool(
451-
project_id, tool_name, unity_instance_hint, tool_params
448+
if not isinstance(tool_params, dict):
449+
return JSONResponse(
450+
{"success": False,
451+
"error": "Tool parameters must be an object/dict"},
452+
status_code=400,
453+
)
454+
455+
# Prefer a concrete hash for project-scoped tools.
456+
unity_instance_hint = unity_instance
457+
if session_details and session_details.hash:
458+
unity_instance_hint = session_details.hash
459+
460+
project_id = resolve_project_id_for_unity_instance(
461+
unity_instance_hint)
462+
if not project_id:
463+
return JSONResponse(
464+
{"success": False,
465+
"error": "Could not resolve project id for custom tool"},
466+
status_code=400,
452467
)
453-
return JSONResponse(result.model_dump())
468+
469+
service = CustomToolService.get_instance()
470+
result = await service.execute_tool(
471+
project_id, tool_name, unity_instance_hint, tool_params
472+
)
473+
return JSONResponse(result.model_dump())
454474

455475
# Send command to Unity
456476
result = await PluginHub.send_command(session_id, command_type, params)

0 commit comments

Comments
 (0)