@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.
- package/README.md +187 -0
- package/addons/godot_mcp/command_handler.gd +161 -0
- package/addons/godot_mcp/command_handler.gd.uid +1 -0
- package/addons/godot_mcp/commands/base_command_processor.gd +221 -0
- package/addons/godot_mcp/commands/base_command_processor.gd.uid +1 -0
- package/addons/godot_mcp/commands/debugger_commands.gd +221 -0
- package/addons/godot_mcp/commands/debugger_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/editor_commands.gd +237 -0
- package/addons/godot_mcp/commands/editor_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/editor_script_commands.gd +365 -0
- package/addons/godot_mcp/commands/editor_script_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/input_commands.gd +337 -0
- package/addons/godot_mcp/commands/input_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/node_commands.gd +222 -0
- package/addons/godot_mcp/commands/node_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/project_commands.gd +298 -0
- package/addons/godot_mcp/commands/project_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/scene_commands.gd +337 -0
- package/addons/godot_mcp/commands/scene_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/script_commands.gd +349 -0
- package/addons/godot_mcp/commands/script_commands.gd.uid +1 -0
- package/addons/godot_mcp/mcp_asset_commands.gd +153 -0
- package/addons/godot_mcp/mcp_asset_commands.gd.uid +1 -0
- package/addons/godot_mcp/mcp_debug_output_publisher.gd +1669 -0
- package/addons/godot_mcp/mcp_debug_output_publisher.gd.uid +1 -0
- package/addons/godot_mcp/mcp_debugger_bridge.gd +1455 -0
- package/addons/godot_mcp/mcp_debugger_bridge.gd.uid +1 -0
- package/addons/godot_mcp/mcp_enhanced_commands.gd +1083 -0
- package/addons/godot_mcp/mcp_enhanced_commands.gd.uid +1 -0
- package/addons/godot_mcp/mcp_input_handler.gd +545 -0
- package/addons/godot_mcp/mcp_input_handler.gd.uid +1 -0
- package/addons/godot_mcp/mcp_runtime_debugger_bridge.gd +464 -0
- package/addons/godot_mcp/mcp_runtime_debugger_bridge.gd.uid +1 -0
- package/addons/godot_mcp/mcp_script_resource_commands.gd +165 -0
- package/addons/godot_mcp/mcp_script_resource_commands.gd.uid +1 -0
- package/addons/godot_mcp/mcp_server.gd +260 -0
- package/addons/godot_mcp/mcp_server.gd.uid +1 -0
- package/addons/godot_mcp/plugin.cfg +7 -0
- package/addons/godot_mcp/runtime_debugger.gd +81 -0
- package/addons/godot_mcp/runtime_debugger.gd.uid +1 -0
- package/addons/godot_mcp/ui/mcp_panel.gd +94 -0
- package/addons/godot_mcp/ui/mcp_panel.gd.uid +1 -0
- package/addons/godot_mcp/ui/mcp_panel.tscn +96 -0
- package/addons/godot_mcp/utils/node_utils.gd +82 -0
- package/addons/godot_mcp/utils/node_utils.gd.uid +1 -0
- package/addons/godot_mcp/utils/resource_utils.gd +81 -0
- package/addons/godot_mcp/utils/resource_utils.gd.uid +1 -0
- package/addons/godot_mcp/utils/script_utils.gd +114 -0
- package/addons/godot_mcp/utils/script_utils.gd.uid +1 -0
- package/addons/godot_mcp/websocket_server.gd +197 -0
- package/addons/godot_mcp/websocket_server.gd.uid +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +561 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +156 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/asset_resources.d.ts +29 -0
- package/dist/resources/asset_resources.js +145 -0
- package/dist/resources/asset_resources.js.map +1 -0
- package/dist/resources/debug_resources.d.ts +11 -0
- package/dist/resources/debug_resources.js +106 -0
- package/dist/resources/debug_resources.js.map +1 -0
- package/dist/resources/debugger_resources.d.ts +62 -0
- package/dist/resources/debugger_resources.js +201 -0
- package/dist/resources/debugger_resources.js.map +1 -0
- package/dist/resources/editor_resources.d.ts +47 -0
- package/dist/resources/editor_resources.js +155 -0
- package/dist/resources/editor_resources.js.map +1 -0
- package/dist/resources/project_resources.d.ts +33 -0
- package/dist/resources/project_resources.js +137 -0
- package/dist/resources/project_resources.js.map +1 -0
- package/dist/resources/scene_resources.d.ts +33 -0
- package/dist/resources/scene_resources.js +160 -0
- package/dist/resources/scene_resources.js.map +1 -0
- package/dist/resources/script_resources.d.ts +51 -0
- package/dist/resources/script_resources.js +203 -0
- package/dist/resources/script_resources.js.map +1 -0
- package/dist/tools/asset_tools.d.ts +5 -0
- package/dist/tools/asset_tools.js +125 -0
- package/dist/tools/asset_tools.js.map +1 -0
- package/dist/tools/debugger_tools.d.ts +2 -0
- package/dist/tools/debugger_tools.js +342 -0
- package/dist/tools/debugger_tools.js.map +1 -0
- package/dist/tools/editor_tools.d.ts +2 -0
- package/dist/tools/editor_tools.js +165 -0
- package/dist/tools/editor_tools.js.map +1 -0
- package/dist/tools/enhanced_tools.d.ts +5 -0
- package/dist/tools/enhanced_tools.js +706 -0
- package/dist/tools/enhanced_tools.js.map +1 -0
- package/dist/tools/input_tools.d.ts +2 -0
- package/dist/tools/input_tools.js +408 -0
- package/dist/tools/input_tools.js.map +1 -0
- package/dist/tools/node_tools.d.ts +5 -0
- package/dist/tools/node_tools.js +217 -0
- package/dist/tools/node_tools.js.map +1 -0
- package/dist/tools/project_tools.d.ts +5 -0
- package/dist/tools/project_tools.js +162 -0
- package/dist/tools/project_tools.js.map +1 -0
- package/dist/tools/scene_tools.d.ts +5 -0
- package/dist/tools/scene_tools.js +260 -0
- package/dist/tools/scene_tools.js.map +1 -0
- package/dist/tools/script_resource_tools.d.ts +5 -0
- package/dist/tools/script_resource_tools.js +5 -0
- package/dist/tools/script_resource_tools.js.map +1 -0
- package/dist/tools/script_tools.d.ts +5 -0
- package/dist/tools/script_tools.js +154 -0
- package/dist/tools/script_tools.js.map +1 -0
- package/dist/utils/godot_connection.d.ts +30 -0
- package/dist/utils/godot_connection.js +285 -0
- package/dist/utils/godot_connection.js.map +1 -0
- package/dist/utils/types.d.ts +16 -0
- package/dist/utils/types.js +2 -0
- package/dist/utils/types.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name MCPDebuggerCommands
|
|
3
|
+
extends MCPBaseCommandProcessor
|
|
4
|
+
|
|
5
|
+
# Reference to the debugger bridge provided by the plugin
|
|
6
|
+
var _debugger_bridge = null
|
|
7
|
+
|
|
8
|
+
func _ready():
|
|
9
|
+
_debugger_bridge = _get_debugger_bridge()
|
|
10
|
+
|
|
11
|
+
if _debugger_bridge:
|
|
12
|
+
# Connect signals so the bridge can forward debugger events to MCP
|
|
13
|
+
_debugger_bridge.breakpoint_hit.connect(_on_breakpoint_hit)
|
|
14
|
+
_debugger_bridge.execution_paused.connect(_on_execution_paused)
|
|
15
|
+
_debugger_bridge.execution_resumed.connect(_on_execution_resumed)
|
|
16
|
+
_debugger_bridge.stack_frame_changed.connect(_on_stack_frame_changed)
|
|
17
|
+
_debugger_bridge.breakpoint_set.connect(_on_breakpoint_set)
|
|
18
|
+
_debugger_bridge.breakpoint_removed.connect(_on_breakpoint_removed)
|
|
19
|
+
else:
|
|
20
|
+
print('[MCPDebuggerCommands] Warning: Could not get debugger bridge reference')
|
|
21
|
+
|
|
22
|
+
func _get_debugger_bridge():
|
|
23
|
+
if Engine.has_meta('MCPDebuggerBridge'):
|
|
24
|
+
return Engine.get_meta('MCPDebuggerBridge')
|
|
25
|
+
|
|
26
|
+
var plugin = Engine.get_meta('GodotMCPPlugin')
|
|
27
|
+
if plugin and plugin.has_method('get_debugger_bridge'):
|
|
28
|
+
return plugin.get_debugger_bridge()
|
|
29
|
+
|
|
30
|
+
return null
|
|
31
|
+
|
|
32
|
+
func _ensure_bridge(client_id: int, command_id: String) -> bool:
|
|
33
|
+
if _debugger_bridge:
|
|
34
|
+
return true
|
|
35
|
+
|
|
36
|
+
_send_error(client_id, 'Debugger bridge not available', command_id)
|
|
37
|
+
return false
|
|
38
|
+
|
|
39
|
+
func _normalize_script_path(script_path: String) -> String:
|
|
40
|
+
if script_path.is_empty():
|
|
41
|
+
return script_path
|
|
42
|
+
|
|
43
|
+
if script_path.begins_with('res://'):
|
|
44
|
+
return script_path
|
|
45
|
+
|
|
46
|
+
if script_path.begins_with('/'):
|
|
47
|
+
return 'res://' + script_path.substr(1)
|
|
48
|
+
|
|
49
|
+
return 'res://' + script_path
|
|
50
|
+
|
|
51
|
+
func _forward_bridge_result(client_id: int, command_id: String, result: Variant, failure_message: String) -> void:
|
|
52
|
+
if result is Dictionary and not result.get('success', true):
|
|
53
|
+
_send_error(client_id, result.get('message', failure_message), command_id)
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
_send_success(client_id, result, command_id)
|
|
57
|
+
|
|
58
|
+
func _call_breakpoint_operation(client_id: int, params: Dictionary, command_id: String, method_name: String, failure_message: String) -> void:
|
|
59
|
+
if not _ensure_bridge(client_id, command_id):
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
var script_path: String = params.get('script_path', '')
|
|
63
|
+
var line: int = params.get('line', -1)
|
|
64
|
+
|
|
65
|
+
if script_path.is_empty():
|
|
66
|
+
_send_error(client_id, 'script_path parameter is required', command_id)
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
if line < 0:
|
|
70
|
+
_send_error(client_id, 'line parameter must be >= 0', command_id)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
script_path = _normalize_script_path(script_path)
|
|
74
|
+
|
|
75
|
+
var result = _debugger_bridge.call(method_name, script_path, line)
|
|
76
|
+
_forward_bridge_result(client_id, command_id, result, failure_message)
|
|
77
|
+
|
|
78
|
+
func _call_bridge_no_args(client_id: int, command_id: String, method_name: String, failure_message: String) -> void:
|
|
79
|
+
if not _ensure_bridge(client_id, command_id):
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
var result = _debugger_bridge.call(method_name)
|
|
83
|
+
_forward_bridge_result(client_id, command_id, result, failure_message)
|
|
84
|
+
|
|
85
|
+
func process_command(client_id: int, command_type: String, params: Dictionary, command_id: String) -> bool:
|
|
86
|
+
match command_type:
|
|
87
|
+
'debugger_set_breakpoint':
|
|
88
|
+
_call_breakpoint_operation(client_id, params, command_id, 'set_breakpoint', 'Failed to set breakpoint')
|
|
89
|
+
return true
|
|
90
|
+
'debugger_remove_breakpoint':
|
|
91
|
+
_call_breakpoint_operation(client_id, params, command_id, 'remove_breakpoint', 'Failed to remove breakpoint')
|
|
92
|
+
return true
|
|
93
|
+
'debugger_get_breakpoints':
|
|
94
|
+
_get_breakpoints(client_id, params, command_id)
|
|
95
|
+
return true
|
|
96
|
+
'debugger_clear_all_breakpoints':
|
|
97
|
+
_call_bridge_no_args(client_id, command_id, 'clear_all_breakpoints', 'Failed to clear all breakpoints')
|
|
98
|
+
return true
|
|
99
|
+
'debugger_pause_execution':
|
|
100
|
+
_call_bridge_no_args(client_id, command_id, 'pause_execution', 'Failed to pause execution')
|
|
101
|
+
return true
|
|
102
|
+
'debugger_resume_execution':
|
|
103
|
+
_call_bridge_no_args(client_id, command_id, 'resume_execution', 'Failed to resume execution')
|
|
104
|
+
return true
|
|
105
|
+
'debugger_step_over':
|
|
106
|
+
_call_bridge_no_args(client_id, command_id, 'step_over', 'Failed to step over')
|
|
107
|
+
return true
|
|
108
|
+
'debugger_step_into':
|
|
109
|
+
_call_bridge_no_args(client_id, command_id, 'step_into', 'Failed to step into')
|
|
110
|
+
return true
|
|
111
|
+
'debugger_get_call_stack':
|
|
112
|
+
await _get_call_stack(client_id, params, command_id)
|
|
113
|
+
return true
|
|
114
|
+
'debugger_get_current_state':
|
|
115
|
+
_get_current_state(client_id, params, command_id)
|
|
116
|
+
return true
|
|
117
|
+
'debugger_enable_events':
|
|
118
|
+
_enable_debugger_events(client_id, params, command_id)
|
|
119
|
+
return true
|
|
120
|
+
'debugger_disable_events':
|
|
121
|
+
_disable_debugger_events(client_id, params, command_id)
|
|
122
|
+
return true
|
|
123
|
+
return false
|
|
124
|
+
|
|
125
|
+
func _get_breakpoints(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
126
|
+
if not _ensure_bridge(client_id, command_id):
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
var result = _debugger_bridge.get_breakpoints()
|
|
130
|
+
_send_success(client_id, result, command_id)
|
|
131
|
+
|
|
132
|
+
func _parse_session_identifier(raw_value) -> Variant:
|
|
133
|
+
var session_id = raw_value
|
|
134
|
+
var value_type := typeof(raw_value)
|
|
135
|
+
match value_type:
|
|
136
|
+
TYPE_INT:
|
|
137
|
+
return raw_value
|
|
138
|
+
TYPE_FLOAT:
|
|
139
|
+
return int(raw_value)
|
|
140
|
+
TYPE_STRING:
|
|
141
|
+
var session_str: String = raw_value
|
|
142
|
+
if session_str.is_valid_int():
|
|
143
|
+
return int(session_str)
|
|
144
|
+
return session_str.strip_edges()
|
|
145
|
+
_:
|
|
146
|
+
return raw_value
|
|
147
|
+
return session_id
|
|
148
|
+
|
|
149
|
+
func _get_call_stack(client_id: int, params: Dictionary, command_id: String):
|
|
150
|
+
if not _ensure_bridge(client_id, command_id):
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
var session_id = null
|
|
154
|
+
if params.has('session_id'):
|
|
155
|
+
session_id = _parse_session_identifier(params['session_id'])
|
|
156
|
+
|
|
157
|
+
var result = await _debugger_bridge.get_call_stack(session_id)
|
|
158
|
+
if typeof(result) == TYPE_DICTIONARY and result.has("error"):
|
|
159
|
+
var message := String(result.get("message", result.get("error", "unknown_call_stack_error")))
|
|
160
|
+
_send_error(client_id, "Failed to capture call stack: %s" % message, command_id)
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
_send_success(client_id, result, command_id)
|
|
164
|
+
|
|
165
|
+
func _get_current_state(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
166
|
+
if not _ensure_bridge(client_id, command_id):
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
var result = _debugger_bridge.get_current_state()
|
|
170
|
+
_send_success(client_id, result, command_id)
|
|
171
|
+
|
|
172
|
+
func _enable_debugger_events(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
173
|
+
if not _ensure_bridge(client_id, command_id):
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
_debugger_bridge.set_client_id(client_id)
|
|
177
|
+
|
|
178
|
+
if not _debugger_bridge._websocket_server:
|
|
179
|
+
_debugger_bridge.set_websocket_server(_websocket_server)
|
|
180
|
+
|
|
181
|
+
_send_success(client_id, {
|
|
182
|
+
'message': 'Debugger events enabled for this client',
|
|
183
|
+
'client_id': client_id,
|
|
184
|
+
}, command_id)
|
|
185
|
+
|
|
186
|
+
func _disable_debugger_events(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
187
|
+
if not _ensure_bridge(client_id, command_id):
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
if _debugger_bridge._current_client_id == client_id:
|
|
191
|
+
_debugger_bridge.set_client_id(-1)
|
|
192
|
+
|
|
193
|
+
_send_success(client_id, {
|
|
194
|
+
'message': 'Debugger events disabled for this client',
|
|
195
|
+
'client_id': client_id,
|
|
196
|
+
}, command_id)
|
|
197
|
+
|
|
198
|
+
# Signal handlers to provide editor-side visibility into debugger activity
|
|
199
|
+
func _on_breakpoint_hit(session_id: int, script_path: String, line: int, stack_info: Dictionary) -> void:
|
|
200
|
+
print('Breakpoint hit in session %s at %s:%d' % [session_id, script_path, line])
|
|
201
|
+
|
|
202
|
+
func _on_execution_paused(session_id: int, reason: String) -> void:
|
|
203
|
+
print('Execution paused in session %s: %s' % [session_id, reason])
|
|
204
|
+
|
|
205
|
+
func _on_execution_resumed(session_id: int) -> void:
|
|
206
|
+
print('Execution resumed in session %s' % session_id)
|
|
207
|
+
|
|
208
|
+
func _on_stack_frame_changed(session_id: int, frame_info: Dictionary) -> void:
|
|
209
|
+
print('Stack frame changed in session %s' % session_id)
|
|
210
|
+
|
|
211
|
+
func _on_breakpoint_set(session_id: int, script_path: String, line: int, success: bool) -> void:
|
|
212
|
+
if success:
|
|
213
|
+
print('Breakpoint set successfully at %s:%d' % [script_path, line])
|
|
214
|
+
else:
|
|
215
|
+
print('Failed to set breakpoint at %s:%d' % [script_path, line])
|
|
216
|
+
|
|
217
|
+
func _on_breakpoint_removed(session_id: int, script_path: String, line: int, success: bool) -> void:
|
|
218
|
+
if success:
|
|
219
|
+
print('Breakpoint removed successfully at %s:%d' % [script_path, line])
|
|
220
|
+
else:
|
|
221
|
+
print('Failed to remove breakpoint at %s:%d' % [script_path, line])
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://b3j3sw8hamog6
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name MCPEditorCommands
|
|
3
|
+
extends MCPBaseCommandProcessor
|
|
4
|
+
|
|
5
|
+
func process_command(client_id: int, command_type: String, params: Dictionary, command_id: String) -> bool:
|
|
6
|
+
match command_type:
|
|
7
|
+
"get_editor_state":
|
|
8
|
+
_get_editor_state(client_id, params, command_id)
|
|
9
|
+
return true
|
|
10
|
+
"get_selected_node":
|
|
11
|
+
_get_selected_node(client_id, params, command_id)
|
|
12
|
+
return true
|
|
13
|
+
"create_resource":
|
|
14
|
+
_create_resource(client_id, params, command_id)
|
|
15
|
+
return true
|
|
16
|
+
"reload_project":
|
|
17
|
+
_reload_project(client_id, params, command_id)
|
|
18
|
+
return true
|
|
19
|
+
"reload_scene":
|
|
20
|
+
_reload_scene(client_id, params, command_id)
|
|
21
|
+
return true
|
|
22
|
+
"rescan_filesystem":
|
|
23
|
+
_rescan_filesystem(client_id, params, command_id)
|
|
24
|
+
return true
|
|
25
|
+
return false # Command not handled
|
|
26
|
+
|
|
27
|
+
func _get_editor_state(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
28
|
+
# Get editor plugin and interfaces
|
|
29
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
30
|
+
if not plugin:
|
|
31
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
32
|
+
|
|
33
|
+
var editor_interface = plugin.get_editor_interface()
|
|
34
|
+
|
|
35
|
+
var state = {
|
|
36
|
+
"current_scene": "",
|
|
37
|
+
"current_script": "",
|
|
38
|
+
"selected_nodes": [],
|
|
39
|
+
"is_playing": editor_interface.is_playing_scene()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Get current scene
|
|
43
|
+
var edited_scene_root = editor_interface.get_edited_scene_root()
|
|
44
|
+
if edited_scene_root:
|
|
45
|
+
state["current_scene"] = edited_scene_root.scene_file_path
|
|
46
|
+
|
|
47
|
+
# Get current script if any is being edited
|
|
48
|
+
var script_editor = editor_interface.get_script_editor()
|
|
49
|
+
var current_script = script_editor.get_current_script()
|
|
50
|
+
if current_script:
|
|
51
|
+
state["current_script"] = current_script.resource_path
|
|
52
|
+
|
|
53
|
+
# Get selected nodes
|
|
54
|
+
var selection = editor_interface.get_selection()
|
|
55
|
+
var selected_nodes = selection.get_selected_nodes()
|
|
56
|
+
|
|
57
|
+
for node in selected_nodes:
|
|
58
|
+
state["selected_nodes"].append({
|
|
59
|
+
"name": node.name,
|
|
60
|
+
"path": str(node.get_path())
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
_send_success(client_id, state, command_id)
|
|
64
|
+
|
|
65
|
+
func _get_selected_node(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
66
|
+
# Get editor plugin and interfaces
|
|
67
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
68
|
+
if not plugin:
|
|
69
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
70
|
+
|
|
71
|
+
var editor_interface = plugin.get_editor_interface()
|
|
72
|
+
var selection = editor_interface.get_selection()
|
|
73
|
+
var selected_nodes = selection.get_selected_nodes()
|
|
74
|
+
|
|
75
|
+
if selected_nodes.size() == 0:
|
|
76
|
+
return _send_success(client_id, {
|
|
77
|
+
"selected": false,
|
|
78
|
+
"message": "No node is currently selected"
|
|
79
|
+
}, command_id)
|
|
80
|
+
|
|
81
|
+
var node = selected_nodes[0] # Get the first selected node
|
|
82
|
+
|
|
83
|
+
# Get node info
|
|
84
|
+
var node_data = {
|
|
85
|
+
"selected": true,
|
|
86
|
+
"name": node.name,
|
|
87
|
+
"type": node.get_class(),
|
|
88
|
+
"path": str(node.get_path())
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Get script info if available
|
|
92
|
+
var script = node.get_script()
|
|
93
|
+
if script:
|
|
94
|
+
node_data["script_path"] = script.resource_path
|
|
95
|
+
|
|
96
|
+
# Get important properties
|
|
97
|
+
var properties = {}
|
|
98
|
+
var property_list = node.get_property_list()
|
|
99
|
+
|
|
100
|
+
for prop in property_list:
|
|
101
|
+
var name = prop["name"]
|
|
102
|
+
if not name.begins_with("_"): # Skip internal properties
|
|
103
|
+
# Only include some common properties to avoid overwhelming data
|
|
104
|
+
if name in ["position", "rotation", "scale", "visible", "modulate", "z_index"]:
|
|
105
|
+
properties[name] = node.get(name)
|
|
106
|
+
|
|
107
|
+
node_data["properties"] = properties
|
|
108
|
+
|
|
109
|
+
_send_success(client_id, node_data, command_id)
|
|
110
|
+
|
|
111
|
+
func _create_resource(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
112
|
+
var resource_type = params.get("resource_type", "")
|
|
113
|
+
var resource_path = params.get("resource_path", "")
|
|
114
|
+
var properties = params.get("properties", {})
|
|
115
|
+
|
|
116
|
+
# Validation
|
|
117
|
+
if resource_type.is_empty():
|
|
118
|
+
return _send_error(client_id, "Resource type cannot be empty", command_id)
|
|
119
|
+
|
|
120
|
+
if resource_path.is_empty():
|
|
121
|
+
return _send_error(client_id, "Resource path cannot be empty", command_id)
|
|
122
|
+
|
|
123
|
+
# Make sure we have an absolute path
|
|
124
|
+
if not resource_path.begins_with("res://"):
|
|
125
|
+
resource_path = "res://" + resource_path
|
|
126
|
+
|
|
127
|
+
# Get editor interface
|
|
128
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
129
|
+
if not plugin:
|
|
130
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
131
|
+
|
|
132
|
+
var editor_interface = plugin.get_editor_interface()
|
|
133
|
+
|
|
134
|
+
# Create the resource
|
|
135
|
+
var resource
|
|
136
|
+
|
|
137
|
+
if ClassDB.class_exists(resource_type):
|
|
138
|
+
if ClassDB.is_parent_class(resource_type, "Resource"):
|
|
139
|
+
resource = ClassDB.instantiate(resource_type)
|
|
140
|
+
if not resource:
|
|
141
|
+
return _send_error(client_id, "Failed to instantiate resource: %s" % resource_type, command_id)
|
|
142
|
+
else:
|
|
143
|
+
return _send_error(client_id, "Type is not a Resource: %s" % resource_type, command_id)
|
|
144
|
+
else:
|
|
145
|
+
return _send_error(client_id, "Invalid resource type: %s" % resource_type, command_id)
|
|
146
|
+
|
|
147
|
+
# Set properties
|
|
148
|
+
for key in properties:
|
|
149
|
+
resource.set(key, properties[key])
|
|
150
|
+
|
|
151
|
+
# Create directory if needed
|
|
152
|
+
var dir = resource_path.get_base_dir()
|
|
153
|
+
if not DirAccess.dir_exists_absolute(dir):
|
|
154
|
+
var err = DirAccess.make_dir_recursive_absolute(dir)
|
|
155
|
+
if err != OK:
|
|
156
|
+
return _send_error(client_id, "Failed to create directory: %s (Error code: %d)" % [dir, err], command_id)
|
|
157
|
+
|
|
158
|
+
# Save the resource
|
|
159
|
+
var result = ResourceSaver.save(resource, resource_path)
|
|
160
|
+
if result != OK:
|
|
161
|
+
return _send_error(client_id, "Failed to save resource: %d" % result, command_id)
|
|
162
|
+
|
|
163
|
+
# Refresh the filesystem
|
|
164
|
+
editor_interface.get_resource_filesystem().scan()
|
|
165
|
+
|
|
166
|
+
_send_success(client_id, {
|
|
167
|
+
"resource_path": resource_path,
|
|
168
|
+
"resource_type": resource_type
|
|
169
|
+
}, command_id)
|
|
170
|
+
|
|
171
|
+
func _reload_project(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
172
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
173
|
+
if not plugin:
|
|
174
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
175
|
+
|
|
176
|
+
var editor_interface = plugin.get_editor_interface()
|
|
177
|
+
var save_before_restart: bool = params.get("save", true)
|
|
178
|
+
|
|
179
|
+
# Send success response before restarting (since restart will disconnect)
|
|
180
|
+
_send_success(client_id, {
|
|
181
|
+
"status": "restarting",
|
|
182
|
+
"save": save_before_restart,
|
|
183
|
+
"message": "Godot editor is restarting..."
|
|
184
|
+
}, command_id)
|
|
185
|
+
|
|
186
|
+
# Use call_deferred to allow the response to be sent before restart
|
|
187
|
+
editor_interface.call_deferred("restart_editor", save_before_restart)
|
|
188
|
+
|
|
189
|
+
func _reload_scene(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
190
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
191
|
+
if not plugin:
|
|
192
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
193
|
+
|
|
194
|
+
var editor_interface = plugin.get_editor_interface()
|
|
195
|
+
var scene_path: String = params.get("scene_path", "")
|
|
196
|
+
|
|
197
|
+
# If no scene path provided, reload the current scene
|
|
198
|
+
if scene_path.is_empty():
|
|
199
|
+
var edited_scene_root = editor_interface.get_edited_scene_root()
|
|
200
|
+
if not edited_scene_root:
|
|
201
|
+
return _send_error(client_id, "No scene is currently open in the editor", command_id)
|
|
202
|
+
|
|
203
|
+
scene_path = edited_scene_root.scene_file_path
|
|
204
|
+
if scene_path.is_empty():
|
|
205
|
+
return _send_error(client_id, "Current scene has not been saved yet", command_id)
|
|
206
|
+
|
|
207
|
+
# Validate scene path
|
|
208
|
+
if not scene_path.begins_with("res://"):
|
|
209
|
+
scene_path = "res://" + scene_path
|
|
210
|
+
|
|
211
|
+
if not ResourceLoader.exists(scene_path):
|
|
212
|
+
return _send_error(client_id, "Scene does not exist: %s" % scene_path, command_id)
|
|
213
|
+
|
|
214
|
+
# Reload the scene from disk
|
|
215
|
+
editor_interface.reload_scene_from_path(scene_path)
|
|
216
|
+
|
|
217
|
+
_send_success(client_id, {
|
|
218
|
+
"status": "reloaded",
|
|
219
|
+
"scene_path": scene_path,
|
|
220
|
+
"message": "Scene reloaded from disk"
|
|
221
|
+
}, command_id)
|
|
222
|
+
|
|
223
|
+
func _rescan_filesystem(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
224
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
225
|
+
if not plugin:
|
|
226
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
227
|
+
|
|
228
|
+
var editor_interface = plugin.get_editor_interface()
|
|
229
|
+
var filesystem = editor_interface.get_resource_filesystem()
|
|
230
|
+
|
|
231
|
+
# Trigger filesystem scan
|
|
232
|
+
filesystem.scan()
|
|
233
|
+
|
|
234
|
+
_send_success(client_id, {
|
|
235
|
+
"status": "scanning",
|
|
236
|
+
"message": "Filesystem rescan initiated"
|
|
237
|
+
}, command_id)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://dudosnjgw5kp1
|