@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,222 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name MCPNodeCommands
|
|
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
|
+
"create_node":
|
|
8
|
+
_create_node(client_id, params, command_id)
|
|
9
|
+
return true
|
|
10
|
+
"delete_node":
|
|
11
|
+
_delete_node(client_id, params, command_id)
|
|
12
|
+
return true
|
|
13
|
+
"update_node_property":
|
|
14
|
+
_update_node_property(client_id, params, command_id)
|
|
15
|
+
return true
|
|
16
|
+
"get_node_properties":
|
|
17
|
+
_get_node_properties(client_id, params, command_id)
|
|
18
|
+
return true
|
|
19
|
+
"list_nodes":
|
|
20
|
+
_list_nodes(client_id, params, command_id)
|
|
21
|
+
return true
|
|
22
|
+
return false # Command not handled
|
|
23
|
+
|
|
24
|
+
func _create_node(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
25
|
+
var parent_path = params.get("parent_path", ".")
|
|
26
|
+
var node_type = params.get("node_type", "Node")
|
|
27
|
+
var node_name = params.get("node_name", "NewNode")
|
|
28
|
+
|
|
29
|
+
# Validation
|
|
30
|
+
if not ClassDB.class_exists(node_type):
|
|
31
|
+
return _send_error(client_id, "Invalid node type: %s" % node_type, command_id)
|
|
32
|
+
|
|
33
|
+
# Get editor plugin and interfaces
|
|
34
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
35
|
+
if not plugin:
|
|
36
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
37
|
+
|
|
38
|
+
var editor_interface = plugin.get_editor_interface()
|
|
39
|
+
var edited_scene_root = editor_interface.get_edited_scene_root()
|
|
40
|
+
|
|
41
|
+
if not edited_scene_root:
|
|
42
|
+
return _send_error(client_id, "No scene is currently being edited", command_id)
|
|
43
|
+
|
|
44
|
+
# Get the parent node using the editor node helper
|
|
45
|
+
var parent = _get_editor_node(parent_path)
|
|
46
|
+
if not parent:
|
|
47
|
+
return _send_error(client_id, "Parent node not found: %s" % parent_path, command_id)
|
|
48
|
+
|
|
49
|
+
# Create the node
|
|
50
|
+
var node
|
|
51
|
+
if ClassDB.can_instantiate(node_type):
|
|
52
|
+
node = ClassDB.instantiate(node_type)
|
|
53
|
+
else:
|
|
54
|
+
return _send_error(client_id, "Cannot instantiate node of type: %s" % node_type, command_id)
|
|
55
|
+
|
|
56
|
+
if not node:
|
|
57
|
+
return _send_error(client_id, "Failed to create node of type: %s" % node_type, command_id)
|
|
58
|
+
|
|
59
|
+
# Set the node name
|
|
60
|
+
node.name = node_name
|
|
61
|
+
|
|
62
|
+
# Add the node to the parent
|
|
63
|
+
parent.add_child(node)
|
|
64
|
+
|
|
65
|
+
# Set owner for proper serialization
|
|
66
|
+
node.owner = edited_scene_root
|
|
67
|
+
|
|
68
|
+
# Mark the scene as modified
|
|
69
|
+
_mark_scene_modified()
|
|
70
|
+
|
|
71
|
+
_send_success(client_id, {
|
|
72
|
+
"node_path": parent_path + "/" + node_name
|
|
73
|
+
}, command_id)
|
|
74
|
+
|
|
75
|
+
func _delete_node(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
76
|
+
var node_path = params.get("node_path", "")
|
|
77
|
+
|
|
78
|
+
# Validation
|
|
79
|
+
if node_path.is_empty():
|
|
80
|
+
return _send_error(client_id, "Node path cannot be empty", command_id)
|
|
81
|
+
|
|
82
|
+
# Get editor plugin and interfaces
|
|
83
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
84
|
+
if not plugin:
|
|
85
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
86
|
+
|
|
87
|
+
var editor_interface = plugin.get_editor_interface()
|
|
88
|
+
var edited_scene_root = editor_interface.get_edited_scene_root()
|
|
89
|
+
|
|
90
|
+
if not edited_scene_root:
|
|
91
|
+
return _send_error(client_id, "No scene is currently being edited", command_id)
|
|
92
|
+
|
|
93
|
+
# Get the node using the editor node helper
|
|
94
|
+
var node = _get_editor_node(node_path)
|
|
95
|
+
if not node:
|
|
96
|
+
return _send_error(client_id, "Node not found: %s" % node_path, command_id)
|
|
97
|
+
|
|
98
|
+
# Cannot delete the root node
|
|
99
|
+
if node == edited_scene_root:
|
|
100
|
+
return _send_error(client_id, "Cannot delete the root node", command_id)
|
|
101
|
+
|
|
102
|
+
# Get parent for operation
|
|
103
|
+
var parent = node.get_parent()
|
|
104
|
+
if not parent:
|
|
105
|
+
return _send_error(client_id, "Node has no parent: %s" % node_path, command_id)
|
|
106
|
+
|
|
107
|
+
# Remove the node
|
|
108
|
+
parent.remove_child(node)
|
|
109
|
+
node.queue_free()
|
|
110
|
+
|
|
111
|
+
# Mark the scene as modified
|
|
112
|
+
_mark_scene_modified()
|
|
113
|
+
|
|
114
|
+
_send_success(client_id, {
|
|
115
|
+
"deleted_node_path": node_path
|
|
116
|
+
}, command_id)
|
|
117
|
+
|
|
118
|
+
func _update_node_property(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
119
|
+
var node_path = params.get("node_path", "")
|
|
120
|
+
var property_name = params.get("property", "")
|
|
121
|
+
var property_value = params.get("value")
|
|
122
|
+
|
|
123
|
+
# Validation
|
|
124
|
+
if node_path.is_empty():
|
|
125
|
+
return _send_error(client_id, "Node path cannot be empty", command_id)
|
|
126
|
+
|
|
127
|
+
if property_name.is_empty():
|
|
128
|
+
return _send_error(client_id, "Property name cannot be empty", command_id)
|
|
129
|
+
|
|
130
|
+
if property_value == null:
|
|
131
|
+
return _send_error(client_id, "Property value cannot be null", command_id)
|
|
132
|
+
|
|
133
|
+
# Get editor plugin and interfaces
|
|
134
|
+
var plugin = Engine.get_meta("GodotMCPPlugin")
|
|
135
|
+
if not plugin:
|
|
136
|
+
return _send_error(client_id, "GodotMCPPlugin not found in Engine metadata", command_id)
|
|
137
|
+
|
|
138
|
+
# Get the node using the editor node helper
|
|
139
|
+
var node = _get_editor_node(node_path)
|
|
140
|
+
if not node:
|
|
141
|
+
return _send_error(client_id, "Node not found: %s" % node_path, command_id)
|
|
142
|
+
|
|
143
|
+
# Check if the property exists
|
|
144
|
+
if not property_name in node:
|
|
145
|
+
return _send_error(client_id, "Property %s does not exist on node %s" % [property_name, node_path], command_id)
|
|
146
|
+
|
|
147
|
+
# Parse property value for Godot types
|
|
148
|
+
var parsed_value = _parse_property_value(property_value)
|
|
149
|
+
|
|
150
|
+
# Get current property value for undo
|
|
151
|
+
var old_value = node.get(property_name)
|
|
152
|
+
|
|
153
|
+
# Get undo/redo system
|
|
154
|
+
var undo_redo = _get_undo_redo()
|
|
155
|
+
if not undo_redo:
|
|
156
|
+
# Fallback method if we can't get undo/redo
|
|
157
|
+
node.set(property_name, parsed_value)
|
|
158
|
+
_mark_scene_modified()
|
|
159
|
+
else:
|
|
160
|
+
# Use undo/redo for proper editor integration
|
|
161
|
+
undo_redo.create_action("Update Property: " + property_name)
|
|
162
|
+
undo_redo.add_do_property(node, property_name, parsed_value)
|
|
163
|
+
undo_redo.add_undo_property(node, property_name, old_value)
|
|
164
|
+
undo_redo.commit_action()
|
|
165
|
+
|
|
166
|
+
# Mark the scene as modified
|
|
167
|
+
_mark_scene_modified()
|
|
168
|
+
|
|
169
|
+
_send_success(client_id, {
|
|
170
|
+
"node_path": node_path,
|
|
171
|
+
"property": property_name,
|
|
172
|
+
"value": property_value,
|
|
173
|
+
"parsed_value": str(parsed_value)
|
|
174
|
+
}, command_id)
|
|
175
|
+
|
|
176
|
+
func _get_node_properties(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
177
|
+
var node_path = params.get("node_path", "")
|
|
178
|
+
|
|
179
|
+
# Validation
|
|
180
|
+
if node_path.is_empty():
|
|
181
|
+
return _send_error(client_id, "Node path cannot be empty", command_id)
|
|
182
|
+
|
|
183
|
+
# Get the node using the editor node helper
|
|
184
|
+
var node = _get_editor_node(node_path)
|
|
185
|
+
if not node:
|
|
186
|
+
return _send_error(client_id, "Node not found: %s" % node_path, command_id)
|
|
187
|
+
|
|
188
|
+
# Get all properties
|
|
189
|
+
var properties = {}
|
|
190
|
+
var property_list = node.get_property_list()
|
|
191
|
+
|
|
192
|
+
for prop in property_list:
|
|
193
|
+
var name = prop["name"]
|
|
194
|
+
if not name.begins_with("_"): # Skip internal properties
|
|
195
|
+
properties[name] = node.get(name)
|
|
196
|
+
|
|
197
|
+
_send_success(client_id, {
|
|
198
|
+
"node_path": node_path,
|
|
199
|
+
"properties": properties
|
|
200
|
+
}, command_id)
|
|
201
|
+
|
|
202
|
+
func _list_nodes(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
203
|
+
var parent_path = params.get("parent_path", ".")
|
|
204
|
+
|
|
205
|
+
# Get the parent node using the editor node helper
|
|
206
|
+
var parent = _get_editor_node(parent_path)
|
|
207
|
+
if not parent:
|
|
208
|
+
return _send_error(client_id, "Parent node not found: %s" % parent_path, command_id)
|
|
209
|
+
|
|
210
|
+
# Get children
|
|
211
|
+
var children = []
|
|
212
|
+
for child in parent.get_children():
|
|
213
|
+
children.append({
|
|
214
|
+
"name": child.name,
|
|
215
|
+
"type": child.get_class(),
|
|
216
|
+
"path": str(child.get_path()).replace(str(parent.get_path()), parent_path)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
_send_success(client_id, {
|
|
220
|
+
"parent_path": parent_path,
|
|
221
|
+
"children": children
|
|
222
|
+
}, command_id)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://b4u0m3t8o03jt
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name MCPProjectCommands
|
|
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_project_info":
|
|
8
|
+
_get_project_info(client_id, params, command_id)
|
|
9
|
+
return true
|
|
10
|
+
"list_project_files":
|
|
11
|
+
_list_project_files(client_id, params, command_id)
|
|
12
|
+
return true
|
|
13
|
+
"get_project_structure":
|
|
14
|
+
_get_project_structure(client_id, params, command_id)
|
|
15
|
+
return true
|
|
16
|
+
"get_project_settings":
|
|
17
|
+
_get_project_settings(client_id, params, command_id)
|
|
18
|
+
return true
|
|
19
|
+
"list_project_resources":
|
|
20
|
+
_list_project_resources(client_id, params, command_id)
|
|
21
|
+
return true
|
|
22
|
+
"run_project":
|
|
23
|
+
_run_project(client_id, params, command_id)
|
|
24
|
+
return true
|
|
25
|
+
"stop_running_project":
|
|
26
|
+
_stop_running_project(client_id, params, command_id)
|
|
27
|
+
return true
|
|
28
|
+
"run_current_scene":
|
|
29
|
+
_run_current_scene(client_id, params, command_id)
|
|
30
|
+
return true
|
|
31
|
+
"run_specific_scene":
|
|
32
|
+
_run_specific_scene(client_id, params, command_id)
|
|
33
|
+
return true
|
|
34
|
+
return false # Command not handled
|
|
35
|
+
|
|
36
|
+
func _get_project_info(client_id: int, _params: Dictionary, command_id: String) -> void:
|
|
37
|
+
var project_name = ProjectSettings.get_setting("application/config/name", "Untitled Project")
|
|
38
|
+
var project_version = ProjectSettings.get_setting("application/config/version", "1.0.0")
|
|
39
|
+
var project_path = ProjectSettings.globalize_path("res://")
|
|
40
|
+
|
|
41
|
+
# Get Godot version info and structure it as expected by the server
|
|
42
|
+
var version_info = Engine.get_version_info()
|
|
43
|
+
print("Raw Godot version info: ", version_info)
|
|
44
|
+
|
|
45
|
+
# Create structured version object with the expected properties
|
|
46
|
+
var structured_version = {
|
|
47
|
+
"major": version_info.get("major", 0),
|
|
48
|
+
"minor": version_info.get("minor", 0),
|
|
49
|
+
"patch": version_info.get("patch", 0)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_send_success(client_id, {
|
|
53
|
+
"project_name": project_name,
|
|
54
|
+
"project_version": project_version,
|
|
55
|
+
"project_path": project_path,
|
|
56
|
+
"godot_version": structured_version,
|
|
57
|
+
"current_scene": get_tree().edited_scene_root.scene_file_path if get_tree().edited_scene_root else ""
|
|
58
|
+
}, command_id)
|
|
59
|
+
|
|
60
|
+
func _list_project_files(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
61
|
+
var extensions = params.get("extensions", [])
|
|
62
|
+
var files = []
|
|
63
|
+
|
|
64
|
+
# Get all files with the specified extensions
|
|
65
|
+
var dir = DirAccess.open("res://")
|
|
66
|
+
if dir:
|
|
67
|
+
_scan_directory(dir, "", extensions, files)
|
|
68
|
+
else:
|
|
69
|
+
return _send_error(client_id, "Failed to open res:// directory", command_id)
|
|
70
|
+
|
|
71
|
+
_send_success(client_id, {
|
|
72
|
+
"files": files
|
|
73
|
+
}, command_id)
|
|
74
|
+
|
|
75
|
+
func _scan_directory(dir: DirAccess, path: String, extensions: Array, files: Array) -> void:
|
|
76
|
+
dir.list_dir_begin()
|
|
77
|
+
var file_name = dir.get_next()
|
|
78
|
+
|
|
79
|
+
while file_name != "":
|
|
80
|
+
if dir.current_is_dir():
|
|
81
|
+
var subdir = DirAccess.open("res://" + path + file_name)
|
|
82
|
+
if subdir:
|
|
83
|
+
_scan_directory(subdir, path + file_name + "/", extensions, files)
|
|
84
|
+
else:
|
|
85
|
+
var file_path = path + file_name
|
|
86
|
+
var has_valid_extension = extensions.is_empty()
|
|
87
|
+
|
|
88
|
+
for ext in extensions:
|
|
89
|
+
if file_name.ends_with(ext):
|
|
90
|
+
has_valid_extension = true
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
if has_valid_extension:
|
|
94
|
+
files.append("res://" + file_path)
|
|
95
|
+
|
|
96
|
+
file_name = dir.get_next()
|
|
97
|
+
|
|
98
|
+
dir.list_dir_end()
|
|
99
|
+
|
|
100
|
+
func _get_project_structure(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
101
|
+
var structure = {
|
|
102
|
+
"directories": [],
|
|
103
|
+
"file_counts": {},
|
|
104
|
+
"total_files": 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
var dir = DirAccess.open("res://")
|
|
108
|
+
if dir:
|
|
109
|
+
_analyze_project_structure(dir, "", structure)
|
|
110
|
+
else:
|
|
111
|
+
return _send_error(client_id, "Failed to open res:// directory", command_id)
|
|
112
|
+
|
|
113
|
+
_send_success(client_id, structure, command_id)
|
|
114
|
+
|
|
115
|
+
func _analyze_project_structure(dir: DirAccess, path: String, structure: Dictionary) -> void:
|
|
116
|
+
dir.list_dir_begin()
|
|
117
|
+
var file_name = dir.get_next()
|
|
118
|
+
|
|
119
|
+
while file_name != "":
|
|
120
|
+
if dir.current_is_dir():
|
|
121
|
+
var dir_path = path + file_name + "/"
|
|
122
|
+
structure["directories"].append("res://" + dir_path)
|
|
123
|
+
|
|
124
|
+
var subdir = DirAccess.open("res://" + dir_path)
|
|
125
|
+
if subdir:
|
|
126
|
+
_analyze_project_structure(subdir, dir_path, structure)
|
|
127
|
+
else:
|
|
128
|
+
structure["total_files"] += 1
|
|
129
|
+
|
|
130
|
+
var extension = file_name.get_extension()
|
|
131
|
+
if extension in structure["file_counts"]:
|
|
132
|
+
structure["file_counts"][extension] += 1
|
|
133
|
+
else:
|
|
134
|
+
structure["file_counts"][extension] = 1
|
|
135
|
+
|
|
136
|
+
file_name = dir.get_next()
|
|
137
|
+
|
|
138
|
+
dir.list_dir_end()
|
|
139
|
+
|
|
140
|
+
func _get_project_settings(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
141
|
+
# Get relevant project settings
|
|
142
|
+
var settings = {
|
|
143
|
+
"project_name": ProjectSettings.get_setting("application/config/name", "Untitled Project"),
|
|
144
|
+
"project_version": ProjectSettings.get_setting("application/config/version", "1.0.0"),
|
|
145
|
+
"display": {
|
|
146
|
+
"width": ProjectSettings.get_setting("display/window/size/viewport_width", 1024),
|
|
147
|
+
"height": ProjectSettings.get_setting("display/window/size/viewport_height", 600),
|
|
148
|
+
"mode": ProjectSettings.get_setting("display/window/size/mode", 0),
|
|
149
|
+
"resizable": ProjectSettings.get_setting("display/window/size/resizable", true)
|
|
150
|
+
},
|
|
151
|
+
"physics": {
|
|
152
|
+
"2d": {
|
|
153
|
+
"default_gravity": ProjectSettings.get_setting("physics/2d/default_gravity", 980)
|
|
154
|
+
},
|
|
155
|
+
"3d": {
|
|
156
|
+
"default_gravity": ProjectSettings.get_setting("physics/3d/default_gravity", 9.8)
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"rendering": {
|
|
160
|
+
"quality": {
|
|
161
|
+
"msaa": ProjectSettings.get_setting("rendering/anti_aliasing/quality/msaa_2d", 0)
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"input_map": {}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# Get input mappings
|
|
168
|
+
var input_map = ProjectSettings.get_setting("input")
|
|
169
|
+
if input_map:
|
|
170
|
+
settings["input_map"] = input_map
|
|
171
|
+
|
|
172
|
+
_send_success(client_id, settings, command_id)
|
|
173
|
+
|
|
174
|
+
func _list_project_resources(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
175
|
+
var resources = {
|
|
176
|
+
"scenes": [],
|
|
177
|
+
"scripts": [],
|
|
178
|
+
"textures": [],
|
|
179
|
+
"audio": [],
|
|
180
|
+
"models": [],
|
|
181
|
+
"resources": []
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
var dir = DirAccess.open("res://")
|
|
185
|
+
if dir:
|
|
186
|
+
_scan_resources(dir, "", resources)
|
|
187
|
+
else:
|
|
188
|
+
return _send_error(client_id, "Failed to open res:// directory", command_id)
|
|
189
|
+
|
|
190
|
+
_send_success(client_id, resources, command_id)
|
|
191
|
+
|
|
192
|
+
func _scan_resources(dir: DirAccess, path: String, resources: Dictionary) -> void:
|
|
193
|
+
dir.list_dir_begin()
|
|
194
|
+
var file_name = dir.get_next()
|
|
195
|
+
|
|
196
|
+
while file_name != "":
|
|
197
|
+
if dir.current_is_dir():
|
|
198
|
+
var subdir = DirAccess.open("res://" + path + file_name)
|
|
199
|
+
if subdir:
|
|
200
|
+
_scan_resources(subdir, path + file_name + "/", resources)
|
|
201
|
+
else:
|
|
202
|
+
var file_path = "res://" + path + file_name
|
|
203
|
+
|
|
204
|
+
# Categorize by extension
|
|
205
|
+
if file_name.ends_with(".tscn") or file_name.ends_with(".scn"):
|
|
206
|
+
resources["scenes"].append(file_path)
|
|
207
|
+
elif file_name.ends_with(".gd") or file_name.ends_with(".cs"):
|
|
208
|
+
resources["scripts"].append(file_path)
|
|
209
|
+
elif file_name.ends_with(".png") or file_name.ends_with(".jpg") or file_name.ends_with(".jpeg"):
|
|
210
|
+
resources["textures"].append(file_path)
|
|
211
|
+
elif file_name.ends_with(".wav") or file_name.ends_with(".ogg") or file_name.ends_with(".mp3"):
|
|
212
|
+
resources["audio"].append(file_path)
|
|
213
|
+
elif file_name.ends_with(".obj") or file_name.ends_with(".glb") or file_name.ends_with(".gltf"):
|
|
214
|
+
resources["models"].append(file_path)
|
|
215
|
+
elif file_name.ends_with(".tres") or file_name.ends_with(".res"):
|
|
216
|
+
resources["resources"].append(file_path)
|
|
217
|
+
|
|
218
|
+
file_name = dir.get_next()
|
|
219
|
+
|
|
220
|
+
dir.list_dir_end()
|
|
221
|
+
|
|
222
|
+
func _run_project(client_id: int, _params: Dictionary, command_id: String) -> void:
|
|
223
|
+
var editor_interface = _get_editor_interface()
|
|
224
|
+
if not editor_interface:
|
|
225
|
+
return _send_error(client_id, "Editor interface not available", command_id)
|
|
226
|
+
|
|
227
|
+
var main_scene: String = ProjectSettings.get_setting("application/run/main_scene", "")
|
|
228
|
+
if main_scene.is_empty():
|
|
229
|
+
return _send_error(client_id, "No main scene configured in project settings", command_id)
|
|
230
|
+
|
|
231
|
+
editor_interface.play_main_scene()
|
|
232
|
+
_send_success(client_id, {
|
|
233
|
+
"status": "running",
|
|
234
|
+
"scene_path": main_scene
|
|
235
|
+
}, command_id)
|
|
236
|
+
|
|
237
|
+
func _stop_running_project(client_id: int, _params: Dictionary, command_id: String) -> void:
|
|
238
|
+
var editor_interface = _get_editor_interface()
|
|
239
|
+
if not editor_interface:
|
|
240
|
+
return _send_error(client_id, "Editor interface not available", command_id)
|
|
241
|
+
|
|
242
|
+
if not editor_interface.is_playing_scene():
|
|
243
|
+
return _send_success(client_id, {
|
|
244
|
+
"status": "idle",
|
|
245
|
+
"message": "Editor is not currently running a scene"
|
|
246
|
+
}, command_id)
|
|
247
|
+
|
|
248
|
+
editor_interface.stop_playing_scene()
|
|
249
|
+
_send_success(client_id, {
|
|
250
|
+
"status": "stopped"
|
|
251
|
+
}, command_id)
|
|
252
|
+
|
|
253
|
+
func _run_current_scene(client_id: int, _params: Dictionary, command_id: String) -> void:
|
|
254
|
+
var editor_interface = _get_editor_interface()
|
|
255
|
+
if not editor_interface:
|
|
256
|
+
return _send_error(client_id, "Editor interface not available", command_id)
|
|
257
|
+
|
|
258
|
+
var scene_root = editor_interface.get_edited_scene_root()
|
|
259
|
+
if not scene_root:
|
|
260
|
+
return _send_error(client_id, "No scene is currently open in the editor", command_id)
|
|
261
|
+
|
|
262
|
+
var scene_path: String = scene_root.scene_file_path
|
|
263
|
+
if scene_path.is_empty():
|
|
264
|
+
return _send_error(client_id, "Current scene must be saved before it can be run", command_id)
|
|
265
|
+
|
|
266
|
+
editor_interface.play_current_scene()
|
|
267
|
+
_send_success(client_id, {
|
|
268
|
+
"status": "running",
|
|
269
|
+
"scene_path": scene_path
|
|
270
|
+
}, command_id)
|
|
271
|
+
|
|
272
|
+
func _run_specific_scene(client_id: int, params: Dictionary, command_id: String) -> void:
|
|
273
|
+
var editor_interface = _get_editor_interface()
|
|
274
|
+
if not editor_interface:
|
|
275
|
+
return _send_error(client_id, "Editor interface not available", command_id)
|
|
276
|
+
|
|
277
|
+
var scene_path: String = params.get("scene_path", "")
|
|
278
|
+
if scene_path.is_empty():
|
|
279
|
+
return _send_error(client_id, "scene_path parameter is required", command_id)
|
|
280
|
+
|
|
281
|
+
if not ResourceLoader.exists(scene_path):
|
|
282
|
+
return _send_error(client_id, "Scene does not exist: %s" % scene_path, command_id)
|
|
283
|
+
|
|
284
|
+
editor_interface.play_custom_scene(scene_path)
|
|
285
|
+
_send_success(client_id, {
|
|
286
|
+
"status": "running",
|
|
287
|
+
"scene_path": scene_path
|
|
288
|
+
}, command_id)
|
|
289
|
+
|
|
290
|
+
func _get_editor_interface():
|
|
291
|
+
if not Engine.has_meta("GodotMCPPlugin"):
|
|
292
|
+
return null
|
|
293
|
+
|
|
294
|
+
var plugin = Engine.get_meta("GodotMCPPlugin") as EditorPlugin
|
|
295
|
+
if not plugin:
|
|
296
|
+
return null
|
|
297
|
+
|
|
298
|
+
return plugin.get_editor_interface()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://bfww1c3grmul3
|