@baixfeng/godot-mcp-cli 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +187 -0
  2. package/addons/godot_mcp/command_handler.gd +161 -0
  3. package/addons/godot_mcp/command_handler.gd.uid +1 -0
  4. package/addons/godot_mcp/commands/base_command_processor.gd +221 -0
  5. package/addons/godot_mcp/commands/base_command_processor.gd.uid +1 -0
  6. package/addons/godot_mcp/commands/debugger_commands.gd +221 -0
  7. package/addons/godot_mcp/commands/debugger_commands.gd.uid +1 -0
  8. package/addons/godot_mcp/commands/editor_commands.gd +237 -0
  9. package/addons/godot_mcp/commands/editor_commands.gd.uid +1 -0
  10. package/addons/godot_mcp/commands/editor_script_commands.gd +365 -0
  11. package/addons/godot_mcp/commands/editor_script_commands.gd.uid +1 -0
  12. package/addons/godot_mcp/commands/input_commands.gd +337 -0
  13. package/addons/godot_mcp/commands/input_commands.gd.uid +1 -0
  14. package/addons/godot_mcp/commands/node_commands.gd +222 -0
  15. package/addons/godot_mcp/commands/node_commands.gd.uid +1 -0
  16. package/addons/godot_mcp/commands/project_commands.gd +298 -0
  17. package/addons/godot_mcp/commands/project_commands.gd.uid +1 -0
  18. package/addons/godot_mcp/commands/scene_commands.gd +337 -0
  19. package/addons/godot_mcp/commands/scene_commands.gd.uid +1 -0
  20. package/addons/godot_mcp/commands/script_commands.gd +349 -0
  21. package/addons/godot_mcp/commands/script_commands.gd.uid +1 -0
  22. package/addons/godot_mcp/mcp_asset_commands.gd +153 -0
  23. package/addons/godot_mcp/mcp_asset_commands.gd.uid +1 -0
  24. package/addons/godot_mcp/mcp_debug_output_publisher.gd +1669 -0
  25. package/addons/godot_mcp/mcp_debug_output_publisher.gd.uid +1 -0
  26. package/addons/godot_mcp/mcp_debugger_bridge.gd +1455 -0
  27. package/addons/godot_mcp/mcp_debugger_bridge.gd.uid +1 -0
  28. package/addons/godot_mcp/mcp_enhanced_commands.gd +1083 -0
  29. package/addons/godot_mcp/mcp_enhanced_commands.gd.uid +1 -0
  30. package/addons/godot_mcp/mcp_input_handler.gd +545 -0
  31. package/addons/godot_mcp/mcp_input_handler.gd.uid +1 -0
  32. package/addons/godot_mcp/mcp_runtime_debugger_bridge.gd +464 -0
  33. package/addons/godot_mcp/mcp_runtime_debugger_bridge.gd.uid +1 -0
  34. package/addons/godot_mcp/mcp_script_resource_commands.gd +165 -0
  35. package/addons/godot_mcp/mcp_script_resource_commands.gd.uid +1 -0
  36. package/addons/godot_mcp/mcp_server.gd +260 -0
  37. package/addons/godot_mcp/mcp_server.gd.uid +1 -0
  38. package/addons/godot_mcp/plugin.cfg +7 -0
  39. package/addons/godot_mcp/runtime_debugger.gd +81 -0
  40. package/addons/godot_mcp/runtime_debugger.gd.uid +1 -0
  41. package/addons/godot_mcp/ui/mcp_panel.gd +94 -0
  42. package/addons/godot_mcp/ui/mcp_panel.gd.uid +1 -0
  43. package/addons/godot_mcp/ui/mcp_panel.tscn +96 -0
  44. package/addons/godot_mcp/utils/node_utils.gd +82 -0
  45. package/addons/godot_mcp/utils/node_utils.gd.uid +1 -0
  46. package/addons/godot_mcp/utils/resource_utils.gd +81 -0
  47. package/addons/godot_mcp/utils/resource_utils.gd.uid +1 -0
  48. package/addons/godot_mcp/utils/script_utils.gd +114 -0
  49. package/addons/godot_mcp/utils/script_utils.gd.uid +1 -0
  50. package/addons/godot_mcp/websocket_server.gd +197 -0
  51. package/addons/godot_mcp/websocket_server.gd.uid +1 -0
  52. package/dist/cli.d.ts +2 -0
  53. package/dist/cli.js +561 -0
  54. package/dist/cli.js.map +1 -0
  55. package/dist/index.d.ts +1 -0
  56. package/dist/index.js +156 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/resources/asset_resources.d.ts +29 -0
  59. package/dist/resources/asset_resources.js +145 -0
  60. package/dist/resources/asset_resources.js.map +1 -0
  61. package/dist/resources/debug_resources.d.ts +11 -0
  62. package/dist/resources/debug_resources.js +106 -0
  63. package/dist/resources/debug_resources.js.map +1 -0
  64. package/dist/resources/debugger_resources.d.ts +62 -0
  65. package/dist/resources/debugger_resources.js +201 -0
  66. package/dist/resources/debugger_resources.js.map +1 -0
  67. package/dist/resources/editor_resources.d.ts +47 -0
  68. package/dist/resources/editor_resources.js +155 -0
  69. package/dist/resources/editor_resources.js.map +1 -0
  70. package/dist/resources/project_resources.d.ts +33 -0
  71. package/dist/resources/project_resources.js +137 -0
  72. package/dist/resources/project_resources.js.map +1 -0
  73. package/dist/resources/scene_resources.d.ts +33 -0
  74. package/dist/resources/scene_resources.js +160 -0
  75. package/dist/resources/scene_resources.js.map +1 -0
  76. package/dist/resources/script_resources.d.ts +51 -0
  77. package/dist/resources/script_resources.js +203 -0
  78. package/dist/resources/script_resources.js.map +1 -0
  79. package/dist/tools/asset_tools.d.ts +5 -0
  80. package/dist/tools/asset_tools.js +125 -0
  81. package/dist/tools/asset_tools.js.map +1 -0
  82. package/dist/tools/debugger_tools.d.ts +2 -0
  83. package/dist/tools/debugger_tools.js +342 -0
  84. package/dist/tools/debugger_tools.js.map +1 -0
  85. package/dist/tools/editor_tools.d.ts +2 -0
  86. package/dist/tools/editor_tools.js +165 -0
  87. package/dist/tools/editor_tools.js.map +1 -0
  88. package/dist/tools/enhanced_tools.d.ts +5 -0
  89. package/dist/tools/enhanced_tools.js +706 -0
  90. package/dist/tools/enhanced_tools.js.map +1 -0
  91. package/dist/tools/input_tools.d.ts +2 -0
  92. package/dist/tools/input_tools.js +408 -0
  93. package/dist/tools/input_tools.js.map +1 -0
  94. package/dist/tools/node_tools.d.ts +5 -0
  95. package/dist/tools/node_tools.js +217 -0
  96. package/dist/tools/node_tools.js.map +1 -0
  97. package/dist/tools/project_tools.d.ts +5 -0
  98. package/dist/tools/project_tools.js +162 -0
  99. package/dist/tools/project_tools.js.map +1 -0
  100. package/dist/tools/scene_tools.d.ts +5 -0
  101. package/dist/tools/scene_tools.js +260 -0
  102. package/dist/tools/scene_tools.js.map +1 -0
  103. package/dist/tools/script_resource_tools.d.ts +5 -0
  104. package/dist/tools/script_resource_tools.js +5 -0
  105. package/dist/tools/script_resource_tools.js.map +1 -0
  106. package/dist/tools/script_tools.d.ts +5 -0
  107. package/dist/tools/script_tools.js +154 -0
  108. package/dist/tools/script_tools.js.map +1 -0
  109. package/dist/utils/godot_connection.d.ts +30 -0
  110. package/dist/utils/godot_connection.js +285 -0
  111. package/dist/utils/godot_connection.js.map +1 -0
  112. package/dist/utils/types.d.ts +16 -0
  113. package/dist/utils/types.js +2 -0
  114. package/dist/utils/types.js.map +1 -0
  115. package/package.json +58 -0
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # Godot MCP CLI
2
+
3
+ A Command Line Interface (CLI) for AI assistants to interact with Godot Engine, built on the Model Context Protocol (MCP). The CLI is the recommended way to use this tool as it saves context tokens compared to direct MCP integration.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/godot-mcp-cli.svg)](https://www.npmjs.com/package/godot-mcp-cli)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## When to Use What
9
+
10
+ | Method | Best For | Token Usage |
11
+ |--------|----------|-------------|
12
+ | **CLI** (recommended) | AI coding assistants, scripting, automation | Low - only tool output in context |
13
+ | **MCP** | Direct MCP client integration | High - full protocol in context |
14
+
15
+ ## Features
16
+
17
+ ### Core Functionality
18
+ - **Full Godot Project Access**: AI assistants can access and modify scripts, scenes, nodes, and project resources
19
+ - **Flexible Scene Inspection**: Retrieve hierarchy with `get_editor_scene_structure`, including properties and scripts
20
+ - **Runtime Scene Inspection**: Snapshot live scene tree from running games with `get_runtime_scene_structure`
21
+ - **Runtime Expression Evaluation**: Execute expressions in live games using `evaluate_runtime`
22
+ - **Dynamic Script Access**: Read scripts via `godot://script/{path}` and metadata via `godot://script/{path}/metadata`
23
+ - **Script Editing Tools**: Create, edit, or template scripts directly through MCP commands
24
+ - **Node Management**: Create, remove, list, and inspect nodes with automatic path normalization
25
+ - **Scene Operations**: Create, delete, open, and save scenes; query project info and current scene state
26
+ - **Asset Management**: List assets by type and enumerate project files
27
+ - **Project Reload**: Restart editor, reload scenes, or rescan filesystem for external changes
28
+ - **Debug Output Access**: Snapshot logs with `get_debug_output` or tail them live via `stream_debug_output`
29
+ - **Stack Trace Capture**: Pull the editor's Stack Trace text or grab structured frames via `get_stack_trace_panel` / `get_stack_frames_panel`
30
+ - **Editor Automation**: Execute GDScript in editor context via `execute_editor_script`
31
+
32
+ ### **Debugger Integration**
33
+ - **Breakpoint Management**: Set, remove, and list breakpoints across scripts with `debugger_set_breakpoint`
34
+ - **Execution Control**: Pause, resume, and step through code with `debugger_pause_execution`, `debugger_step_over`
35
+ - **Real-time Events**: Live notifications for breakpoint hits and execution changes
36
+ - **Call Stack Inspection**: Access current call stack and frame information with `debugger_get_call_stack`
37
+ - **Session Management**: Support for multiple debug sessions
38
+ - **Runtime Debugging**: Full integration with Godot's debugging system
39
+ - **Event-driven Architecture**: Receive breakpoint hits and execution state changes in real-time
40
+
41
+ ### **Input Simulation**
42
+ - **Action Simulation**: Press, release, and tap input actions (`simulate_action_press`, `simulate_action_tap`)
43
+ - **Mouse Control**: Click, move, and drag operations (`simulate_mouse_click`, `simulate_drag`)
44
+ - **Keyboard Input**: Simulate key presses with modifier support (`simulate_key_press`)
45
+ - **Input Sequences**: Execute complex input combos with precise timing (`simulate_input_sequence`)
46
+ - **Action Discovery**: List all available input actions in the project (`get_input_actions`)
47
+
48
+ ## Installation
49
+
50
+ ### Option 1: Install via npm (Recommended)
51
+
52
+ ```bash
53
+ npm install -g godot-mcp-cli
54
+ ```
55
+
56
+ ### Option 2: Build from Source
57
+
58
+ ```bash
59
+ git clone https://github.com/nguyenchiencong/godot-mcp-cli.git
60
+ cd godot-mcp-cli/server
61
+ npm install
62
+ npm run build
63
+ npm link
64
+ ```
65
+
66
+ ## Quick Setup
67
+
68
+ ### 1. Install the Addon to Your Godot Project
69
+
70
+ ```bash
71
+ godot-mcp install-addon "path/to/your/project"
72
+ ```
73
+
74
+ Or manually copy the `addons/godot_mcp` folder to your Godot project's `addons` directory.
75
+
76
+ ### 2. Enable the Plugin in Godot
77
+
78
+ 1. Open your project in Godot
79
+ 2. Go to Project > Project Settings > Plugins
80
+ 3. Enable the "Godot MCP" plugin
81
+
82
+ ## Using the CLI (Recommended)
83
+
84
+ The CLI is the most efficient way for AI assistants to interact with Godot. It consumes fewer tokens than the MCP protocol.
85
+
86
+ ### Basic Commands
87
+
88
+ ```bash
89
+ # List all available tools
90
+ godot-mcp --list-tools
91
+
92
+ # Get help for a specific tool
93
+ godot-mcp --help get_debug_output
94
+
95
+ # Execute tools
96
+ godot-mcp get_debug_output
97
+ godot-mcp get_project_info
98
+ godot-mcp run_project
99
+
100
+ # With arguments
101
+ godot-mcp debugger_set_breakpoint --script-path res://test_debugger.gd --line 42
102
+ godot-mcp simulate_action_tap --action ui_accept
103
+ godot-mcp simulate_mouse_click --x 400 --y 300
104
+ ```
105
+
106
+ ### CLI Examples
107
+
108
+ ```bash
109
+ # Scene and node operations
110
+ godot-mcp get_current_scene
111
+ godot-mcp get_editor_scene_structure --include-properties true
112
+ godot-mcp list_nodes --parent-path "."
113
+
114
+ # Debugging
115
+ godot-mcp run_project
116
+ godot-mcp debugger_get_current_state
117
+ godot-mcp debugger_pause_execution
118
+ godot-mcp debugger_resume_execution
119
+
120
+ # Input simulation (requires running game)
121
+ godot-mcp get_input_actions
122
+ godot-mcp simulate_action_tap --action "ui_accept"
123
+ godot-mcp simulate_key_press --key "SPACE"
124
+
125
+ # Reload operations
126
+ godot-mcp rescan_filesystem
127
+ godot-mcp reload_scene
128
+ godot-mcp reload_project
129
+ ```
130
+
131
+ For more CLI options, see the [CLI Documentation](docs/cli.md).
132
+
133
+ ## Using the MCP Protocol
134
+
135
+ For direct MCP client integration, add this configuration:
136
+
137
+ ### STDIO Transport (after npm install -g)
138
+ ```json
139
+ {
140
+ "mcpServers": {
141
+ "godot-mcp": {
142
+ "command": "godot-mcp",
143
+ "env": { "MCP_TRANSPORT": "stdio" }
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### STDIO Transport (from source)
150
+ ```json
151
+ {
152
+ "mcpServers": {
153
+ "godot-mcp": {
154
+ "command": "node",
155
+ "args": ["path/to/godot-mcp-cli/server/dist/index.js"],
156
+ "env": { "MCP_TRANSPORT": "stdio" }
157
+ }
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### SSE Transport
163
+ ```json
164
+ {
165
+ "mcpServers": {
166
+ "godot-mcp": {
167
+ "url": "http://localhost:8083/sse"
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ ## Documentation
174
+
175
+ - [Installation Guide](docs/installation-guide.md)
176
+ - [Command Reference](docs/command-reference.md)
177
+ - [Architecture](docs/architecture.md)
178
+ - [CLI Usage](docs/cli.md)
179
+ - [Tool Prompt Guide](docs/tool-prompt-guide.md)
180
+
181
+ ## Contributing
182
+
183
+ Contributions are welcome! Please feel free to submit a Pull Request to the [GitHub repository](https://github.com/nguyenchiencong/godot-mcp-cli).
184
+
185
+ ## License
186
+
187
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,161 @@
1
+ @tool
2
+ class_name MCPCommandHandler
3
+ extends Node
4
+
5
+ var _websocket_server
6
+ var _command_processors = []
7
+
8
+ func _ready():
9
+ print("Command handler initializing...")
10
+ await get_tree().process_frame
11
+ _websocket_server = get_parent()
12
+ print("WebSocket server reference set: ", _websocket_server)
13
+
14
+ # Initialize command processors
15
+ _initialize_command_processors()
16
+
17
+ print("Command handler initialized and ready to process commands")
18
+
19
+ func _initialize_command_processors():
20
+ # Create and add all required command processors
21
+ var node_commands = MCPNodeCommands.new()
22
+ var script_commands = MCPScriptCommands.new()
23
+ var scene_commands = MCPSceneCommands.new()
24
+ var project_commands = MCPProjectCommands.new()
25
+ var editor_commands = MCPEditorCommands.new()
26
+ var editor_script_commands = MCPEditorScriptCommands.new()
27
+ var debugger_commands = MCPDebuggerCommands.new()
28
+ var input_commands = MCPInputCommands.new()
29
+
30
+ # Set server reference for all processors
31
+ node_commands._websocket_server = _websocket_server
32
+ script_commands._websocket_server = _websocket_server
33
+ scene_commands._websocket_server = _websocket_server
34
+ project_commands._websocket_server = _websocket_server
35
+ editor_commands._websocket_server = _websocket_server
36
+ editor_script_commands._websocket_server = _websocket_server
37
+ debugger_commands._websocket_server = _websocket_server
38
+ input_commands._websocket_server = _websocket_server
39
+
40
+ # Add them to our processor list
41
+ _command_processors.append(node_commands)
42
+ _command_processors.append(script_commands)
43
+ _command_processors.append(scene_commands)
44
+ _command_processors.append(project_commands)
45
+ _command_processors.append(editor_commands)
46
+ _command_processors.append(editor_script_commands)
47
+ _command_processors.append(debugger_commands)
48
+ _command_processors.append(input_commands)
49
+
50
+ # Try to load optional command classes
51
+ var script_resource_commands = _try_load_optional_command("res://addons/godot_mcp/mcp_script_resource_commands.gd")
52
+ var enhanced_commands = _try_load_optional_command("res://addons/godot_mcp/mcp_enhanced_commands.gd")
53
+ var asset_commands = _try_load_optional_command("res://addons/godot_mcp/mcp_asset_commands.gd")
54
+
55
+ # Add required processors as children for proper lifecycle management
56
+ add_child(node_commands)
57
+ add_child(script_commands)
58
+ add_child(scene_commands)
59
+ add_child(project_commands)
60
+ add_child(editor_commands)
61
+ add_child(editor_script_commands)
62
+ add_child(debugger_commands)
63
+ add_child(input_commands)
64
+
65
+ print("Command processors initialized:")
66
+ print("- Node Commands")
67
+ print("- Script Commands")
68
+ print("- Scene Commands")
69
+ print("- Project Commands")
70
+ print("- Editor Commands")
71
+ print("- Editor Script Commands")
72
+ print("- Debugger Commands")
73
+ print("- Input Commands")
74
+
75
+ if script_resource_commands:
76
+ print("- Script Resource Commands")
77
+ if enhanced_commands:
78
+ print("- Enhanced Commands")
79
+ if asset_commands:
80
+ print("- Asset Commands")
81
+
82
+ func _try_load_optional_command(path: String) -> Node:
83
+ if FileAccess.file_exists(path):
84
+ var script = load(path)
85
+ if script:
86
+ var command = Node.new()
87
+ command.set_script(script)
88
+ command._websocket_server = _websocket_server
89
+ _command_processors.append(command)
90
+ add_child(command)
91
+ return command
92
+ return null
93
+
94
+ func _handle_command(client_id: int, command: Dictionary) -> void:
95
+ var command_type = command.get("type", "")
96
+ var params = command.get("params", {})
97
+ var command_id = command.get("commandId", "")
98
+
99
+ print("Processing command: %s" % command_type)
100
+
101
+ # Special handling for enhanced commands
102
+ var enhanced_commands = [
103
+ "get_editor_scene_structure",
104
+ "get_runtime_scene_structure",
105
+ "get_debug_output",
106
+ "get_editor_errors",
107
+ "get_stack_trace_panel",
108
+ "get_stack_frames_panel",
109
+ "evaluate_runtime",
110
+ "clear_debug_output",
111
+ "clear_editor_errors",
112
+ "subscribe_debug_output",
113
+ "unsubscribe_debug_output",
114
+ "update_node_transform"
115
+ ]
116
+ if command_type in enhanced_commands:
117
+ # Try to find enhanced commands processor first
118
+ for processor in _command_processors:
119
+ if processor.get_script() and processor.get_script().resource_path.ends_with("mcp_enhanced_commands.gd"):
120
+ var handled = await _call_processor(processor, client_id, command_type, params, command_id)
121
+ if handled:
122
+ print("Command %s handled by Enhanced Commands processor" % command_type)
123
+ return
124
+
125
+ # Try each processor until one handles the command
126
+ for processor in _command_processors:
127
+ var handled = await _call_processor(processor, client_id, command_type, params, command_id)
128
+ if handled:
129
+ print("Command %s handled by %s" % [command_type, processor.get_class()])
130
+ return
131
+
132
+ # If no processor handled the command, send an error
133
+ _send_error(client_id, "Unknown command: %s" % command_type, command_id)
134
+
135
+ func _send_error(client_id: int, message: String, command_id: String) -> void:
136
+ var response = {
137
+ "status": "error",
138
+ "message": message
139
+ }
140
+
141
+ if not command_id.is_empty():
142
+ response["commandId"] = command_id
143
+
144
+ _websocket_server.send_response(client_id, response)
145
+ print("Error: %s" % message)
146
+
147
+ func _processor_requires_await(processor: Node) -> bool:
148
+ if processor is MCPDebuggerCommands:
149
+ return true
150
+ if processor is MCPInputCommands:
151
+ return true
152
+ if processor.get_script():
153
+ var path := String(processor.get_script().resource_path)
154
+ if path.ends_with("mcp_enhanced_commands.gd"):
155
+ return true
156
+ return false
157
+
158
+ func _call_processor(processor: Node, client_id: int, command_type: String, params: Dictionary, command_id: String) -> bool:
159
+ if _processor_requires_await(processor):
160
+ return await processor.process_command(client_id, command_type, params, command_id)
161
+ return processor.process_command(client_id, command_type, params, command_id)
@@ -0,0 +1 @@
1
+ uid://hcu70348uj2f
@@ -0,0 +1,221 @@
1
+ @tool
2
+ class_name MCPBaseCommandProcessor
3
+ extends Node
4
+
5
+ # Signal emitted when a command has completed processing
6
+ signal command_completed(client_id, command_type, result, command_id)
7
+
8
+ # Reference to the server - passed by the command handler
9
+ var _websocket_server = null
10
+
11
+ # Must be implemented by subclasses
12
+ func process_command(client_id: int, command_type: String, params: Dictionary, command_id: String) -> bool:
13
+ push_error("BaseCommandProcessor.process_command called directly")
14
+ return false
15
+
16
+ # Helper functions common to all command processors
17
+ func _send_success(client_id: int, result: Dictionary, command_id: String) -> void:
18
+ var response = {
19
+ "status": "success",
20
+ "result": result
21
+ }
22
+
23
+ if not command_id.is_empty():
24
+ response["commandId"] = command_id
25
+
26
+ # Emit the signal for local processing (useful for testing)
27
+ command_completed.emit(client_id, "success", result, command_id)
28
+
29
+ # Send to websocket if available
30
+ if _websocket_server:
31
+ _websocket_server.send_response(client_id, response)
32
+
33
+ func _send_error(client_id: int, message: String, command_id: String) -> void:
34
+ var response = {
35
+ "status": "error",
36
+ "message": message
37
+ }
38
+
39
+ if not command_id.is_empty():
40
+ response["commandId"] = command_id
41
+
42
+ # Emit the signal for local processing (useful for testing)
43
+ var error_result = {"error": message}
44
+ command_completed.emit(client_id, "error", error_result, command_id)
45
+
46
+ # Send to websocket if available
47
+ if _websocket_server:
48
+ _websocket_server.send_response(client_id, response)
49
+ print("Error: %s" % message)
50
+
51
+ # Common utility methods
52
+ func _get_editor_node(path: String) -> Node:
53
+ var plugin = Engine.get_meta("GodotMCPPlugin")
54
+ if not plugin:
55
+ print("GodotMCPPlugin not found in Engine metadata")
56
+ return null
57
+
58
+ var editor_interface = plugin.get_editor_interface()
59
+ var edited_scene_root = editor_interface.get_edited_scene_root()
60
+
61
+ if not edited_scene_root:
62
+ print("No edited scene found")
63
+ return null
64
+
65
+ var normalized_path = _normalize_node_path(path)
66
+ if normalized_path.is_empty():
67
+ return edited_scene_root
68
+
69
+ var node = edited_scene_root.get_node_or_null(normalized_path)
70
+ if node:
71
+ return node
72
+
73
+ var root_name = edited_scene_root.name
74
+ if normalized_path == root_name:
75
+ return edited_scene_root
76
+
77
+ var root_prefix = root_name + "/"
78
+ if normalized_path.begins_with(root_prefix):
79
+ var trimmed_path = normalized_path.substr(root_prefix.length())
80
+ if trimmed_path.is_empty():
81
+ return edited_scene_root
82
+ node = edited_scene_root.get_node_or_null(trimmed_path)
83
+ if node:
84
+ return node
85
+
86
+ return null
87
+
88
+ # Enhanced version of _get_editor_node to improve node path resolution
89
+ func _get_editor_node_enhanced(path: String) -> Node:
90
+ # First try the standard method
91
+ var node = _get_editor_node(path)
92
+ if node:
93
+ return node
94
+
95
+ # If not found, try additional resolution methods
96
+ var plugin = Engine.get_meta("GodotMCPPlugin")
97
+ if not plugin:
98
+ return null
99
+
100
+ var editor_interface = plugin.get_editor_interface()
101
+ var edited_scene_root = editor_interface.get_edited_scene_root()
102
+
103
+ if not edited_scene_root:
104
+ return null
105
+
106
+ var normalized_path = _normalize_node_path(path)
107
+ if normalized_path.is_empty():
108
+ return edited_scene_root
109
+
110
+ var lower_path = normalized_path.to_lower()
111
+ var root_name_lower = edited_scene_root.name.to_lower()
112
+
113
+ if lower_path == root_name_lower:
114
+ return edited_scene_root
115
+
116
+ var parts = normalized_path.split("/")
117
+ if parts.size() > 1 and parts[0].to_lower() == root_name_lower:
118
+ var sub_path = ""
119
+ for i in range(1, parts.size()):
120
+ if i > 1:
121
+ sub_path += "/"
122
+ sub_path += parts[i]
123
+
124
+ if sub_path.is_empty():
125
+ return edited_scene_root
126
+
127
+ var node_with_root_prefix = edited_scene_root.get_node_or_null(sub_path)
128
+ if node_with_root_prefix:
129
+ return node_with_root_prefix
130
+
131
+ normalized_path = sub_path
132
+ lower_path = normalized_path.to_lower()
133
+
134
+ if normalized_path.find("/") == -1:
135
+ for child in edited_scene_root.get_children():
136
+ if child.name.to_lower() == lower_path:
137
+ return child
138
+
139
+ return null
140
+
141
+ # Helper function to mark a scene as modified
142
+ func _mark_scene_modified() -> void:
143
+ var plugin = Engine.get_meta("GodotMCPPlugin")
144
+ if not plugin:
145
+ print("GodotMCPPlugin not found in Engine metadata")
146
+ return
147
+
148
+ var editor_interface = plugin.get_editor_interface()
149
+ var edited_scene_root = editor_interface.get_edited_scene_root()
150
+
151
+ if edited_scene_root:
152
+ # This internally marks the scene as modified in the editor
153
+ editor_interface.mark_scene_as_unsaved()
154
+
155
+ # Helper function to access the EditorUndoRedoManager
156
+ func _get_undo_redo():
157
+ var plugin = Engine.get_meta("GodotMCPPlugin")
158
+ if not plugin or not plugin.has_method("get_undo_redo"):
159
+ print("Cannot access UndoRedo from plugin")
160
+ return null
161
+
162
+ return plugin.get_undo_redo()
163
+
164
+ # Helper function to parse property values from string to proper Godot types
165
+ func _parse_property_value(value):
166
+ # Only try to parse strings that look like they could be Godot types
167
+ if typeof(value) == TYPE_STRING and (
168
+ value.begins_with("Vector") or
169
+ value.begins_with("Transform") or
170
+ value.begins_with("Rect") or
171
+ value.begins_with("Color") or
172
+ value.begins_with("Quat") or
173
+ value.begins_with("Basis") or
174
+ value.begins_with("Plane") or
175
+ value.begins_with("AABB") or
176
+ value.begins_with("Projection") or
177
+ value.begins_with("Callable") or
178
+ value.begins_with("Signal") or
179
+ value.begins_with("PackedVector") or
180
+ value.begins_with("PackedString") or
181
+ value.begins_with("PackedFloat") or
182
+ value.begins_with("PackedInt") or
183
+ value.begins_with("PackedColor") or
184
+ value.begins_with("PackedByteArray") or
185
+ value.begins_with("Dictionary") or
186
+ value.begins_with("Array")
187
+ ):
188
+ var expression = Expression.new()
189
+ var error = expression.parse(value, [])
190
+
191
+ if error == OK:
192
+ var result = expression.execute([], null, true)
193
+ if not expression.has_execute_failed():
194
+ print("Successfully parsed %s as %s" % [value, result])
195
+ return result
196
+ else:
197
+ print("Failed to execute expression for: %s" % value)
198
+ else:
199
+ print("Failed to parse expression: %s (Error: %d)" % [value, error])
200
+
201
+ # Otherwise, return value as is
202
+ return value
203
+
204
+ func _normalize_node_path(path: String) -> String:
205
+ var normalized = path.strip_edges()
206
+ if normalized.is_empty() or normalized == "." or normalized == "/root":
207
+ return ""
208
+
209
+ while normalized.begins_with("/root/"):
210
+ normalized = normalized.substr(6)
211
+
212
+ while normalized.begins_with("./"):
213
+ normalized = normalized.substr(2)
214
+
215
+ if normalized.begins_with("/"):
216
+ normalized = normalized.substr(1)
217
+
218
+ if normalized.begins_with("."):
219
+ normalized = normalized.substr(1)
220
+
221
+ return normalized.strip_edges()
@@ -0,0 +1 @@
1
+ uid://c3da6achkr43o