@tool extends Node class_name ToolExecutor ## Routes tool invocations to the appropriate handler. var _editor_plugin: EditorPlugin = null var _file_tools: Node var _scene_tools: Node var _script_tools: Node var _project_tools: Node var _asset_tools: Node var _visualizer_tools: Node # Tool name → [handler_node, method_name] var _tool_map: Dictionary = {} var _initialized := false func _init_tools() -> void: """Initialize all tool handlers. Called from set_editor_plugin.""" if _initialized: return _initialized = true _file_tools = preload("res://addons/godot_mcp/tools/file_tools.gd").new() _file_tools.name = "FileTools" add_child(_file_tools) _scene_tools = preload("res://addons/godot_mcp/tools/scene_tools.gd").new() _scene_tools.name = "SceneTools" add_child(_scene_tools) _script_tools = preload("res://addons/godot_mcp/tools/script_tools.gd").new() _script_tools.name = "ScriptTools" add_child(_script_tools) _project_tools = preload("res://addons/godot_mcp/tools/project_tools.gd").new() _project_tools.name = "ProjectTools" add_child(_project_tools) _asset_tools = preload("res://addons/godot_mcp/tools/asset_tools.gd").new() _asset_tools.name = "AssetTools" add_child(_asset_tools) _visualizer_tools = preload("res://addons/godot_mcp/tools/visualizer_tools.gd").new() _visualizer_tools.name = "VisualizerTools" add_child(_visualizer_tools) # Build tool routing map _tool_map = { &"list_dir": [_file_tools, &"list_dir"], &"read_file": [_file_tools, &"read_file"], &"search_project": [_file_tools, &"search_project"], &"create_script": [_file_tools, &"create_script"], &"create_scene": [_scene_tools, &"create_scene"], &"read_scene": [_scene_tools, &"read_scene"], &"add_node": [_scene_tools, &"add_node"], &"remove_node": [_scene_tools, &"remove_node"], &"modify_node_property": [_scene_tools, &"modify_node_property"], &"rename_node": [_scene_tools, &"rename_node"], &"move_node": [_scene_tools, &"move_node"], &"attach_script": [_scene_tools, &"attach_script"], &"detach_script": [_scene_tools, &"detach_script"], &"set_collision_shape": [_scene_tools, &"set_collision_shape"], &"set_sprite_texture": [_scene_tools, &"set_sprite_texture"], &"get_scene_hierarchy": [_scene_tools, &"get_scene_hierarchy"], &"get_scene_node_properties": [_scene_tools, &"get_scene_node_properties"], &"set_scene_node_property": [_scene_tools, &"set_scene_node_property"], &"edit_script": [_script_tools, &"edit_script"], &"validate_script": [_script_tools, &"validate_script"], &"list_scripts": [_script_tools, &"list_scripts"], &"create_folder": [_script_tools, &"create_folder"], &"delete_file": [_script_tools, &"delete_file"], &"rename_file": [_script_tools, &"rename_file"], &"get_project_settings": [_project_tools, &"get_project_settings"], &"list_settings": [_project_tools, &"list_settings"], &"update_project_settings": [_project_tools, &"update_project_settings"], &"get_input_map": [_project_tools, &"get_input_map"], &"configure_input_map": [_project_tools, &"configure_input_map"], &"get_collision_layers": [_project_tools, &"get_collision_layers"], &"setup_autoload": [_project_tools, &"setup_autoload"], &"get_node_properties": [_project_tools, &"get_node_properties"], &"get_console_log": [_project_tools, &"get_console_log"], &"get_errors": [_project_tools, &"get_errors"], &"clear_console_log": [_project_tools, &"clear_console_log"], &"open_in_godot": [_project_tools, &"open_in_godot"], &"scene_tree_dump": [_project_tools, &"scene_tree_dump"], &"generate_2d_asset": [_asset_tools, &"generate_2d_asset"], &"map_project": [_visualizer_tools, &"map_project"], &"map_scenes": [_visualizer_tools, &"map_scenes"], } func set_editor_plugin(plugin: EditorPlugin) -> void: _editor_plugin = plugin # Initialize tools first (must be done synchronously) _init_tools() # Pass editor plugin reference to all tool handlers if _file_tools: _file_tools.set_editor_plugin(plugin) if _scene_tools: _scene_tools.set_editor_plugin(plugin) if _script_tools: _script_tools.set_editor_plugin(plugin) if _project_tools: _project_tools.set_editor_plugin(plugin) if _asset_tools: _asset_tools.set_editor_plugin(plugin) if _visualizer_tools: _visualizer_tools.set_editor_plugin(plugin) # Pass scene_tools reference for visualizer internal scene functions _visualizer_tools.set_scene_tools_ref(_scene_tools) func execute_tool(tool_name: String, args: Dictionary) -> Dictionary: """Execute a tool by name with the given arguments.""" # Handle internal visualizer commands (not exposed as MCP tools) if tool_name.begins_with("visualizer._internal_"): var method: String = tool_name.replace("visualizer.", "") if _visualizer_tools and _visualizer_tools.has_method(method): return _visualizer_tools.call(method, args) else: return {&"ok": false, &"error": "Internal method not found: " + method} if not _tool_map.has(tool_name): return { &"ok": false, &"error": "Unknown tool: %s. Available: %s" % [tool_name, ", ".join(_tool_map.keys())] } var handler: Array = _tool_map[tool_name] var node: Node = handler[0] var method: StringName = handler[1] if not node.has_method(method): return {&"ok": false, &"error": "Tool handler not found: %s.%s" % [node.name, method]} return node.call(method, args) func get_available_tools() -> Array: """Return list of available tool names.""" return _tool_map.keys()