@satelliteoflove/godot-mcp 2.16.1 → 2.17.0

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.
@@ -20,6 +20,7 @@ func setup(plugin: EditorPlugin) -> void:
20
20
  _register_handler(MCPResourceCommands.new(), plugin)
21
21
  _register_handler(MCPScene3DCommands.new(), plugin)
22
22
  _register_handler(MCPInputCommands.new(), plugin)
23
+ _register_handler(MCPProfilerCommands.new(), plugin)
23
24
 
24
25
 
25
26
  func _register_handler(handler: MCPBaseCommand, plugin: EditorPlugin) -> void:
@@ -3,21 +3,16 @@ extends MCPBaseCommand
3
3
  class_name MCPDebugCommands
4
4
 
5
5
  const DEBUG_OUTPUT_TIMEOUT := 5.0
6
- const PERFORMANCE_METRICS_TIMEOUT := 5.0
7
6
 
8
7
  var _debug_output_result: PackedStringArray = []
9
8
  var _debug_output_pending: bool = false
10
9
 
11
- var _performance_metrics_result: Dictionary = {}
12
- var _performance_metrics_pending: bool = false
13
-
14
10
 
15
11
  func get_commands() -> Dictionary:
16
12
  return {
17
13
  "run_project": run_project,
18
14
  "stop_project": stop_project,
19
15
  "get_debug_output": get_debug_output,
20
- "get_performance_metrics": get_performance_metrics,
21
16
  "get_log_messages": get_log_messages,
22
17
  "get_errors": get_errors,
23
18
  "get_stack_trace": get_stack_trace,
@@ -100,37 +95,6 @@ func _on_debug_output_received(output: PackedStringArray) -> void:
100
95
  _debug_output_result = output
101
96
 
102
97
 
103
- func get_performance_metrics(_params: Dictionary) -> Dictionary:
104
- if not EditorInterface.is_playing_scene():
105
- return _error("NOT_RUNNING", "No game is currently running")
106
-
107
- var debugger_plugin = _plugin.get_debugger_plugin() if _plugin else null
108
- if debugger_plugin == null or not debugger_plugin.has_active_session():
109
- return _error("NO_SESSION", "No active debug session")
110
-
111
- _performance_metrics_pending = true
112
- _performance_metrics_result = {}
113
-
114
- debugger_plugin.performance_metrics_received.connect(_on_performance_metrics_received, CONNECT_ONE_SHOT)
115
- debugger_plugin.request_performance_metrics()
116
-
117
- var start_time := Time.get_ticks_msec()
118
- while _performance_metrics_pending:
119
- await Engine.get_main_loop().process_frame
120
- if (Time.get_ticks_msec() - start_time) / 1000.0 > PERFORMANCE_METRICS_TIMEOUT:
121
- _performance_metrics_pending = false
122
- if debugger_plugin.performance_metrics_received.is_connected(_on_performance_metrics_received):
123
- debugger_plugin.performance_metrics_received.disconnect(_on_performance_metrics_received)
124
- return _error("TIMEOUT", "Timed out waiting for performance metrics")
125
-
126
- return _success(_performance_metrics_result)
127
-
128
-
129
- func _on_performance_metrics_received(metrics: Dictionary) -> void:
130
- _performance_metrics_pending = false
131
- _performance_metrics_result = metrics
132
-
133
-
134
98
  func get_log_messages(params: Dictionary) -> Dictionary:
135
99
  var clear: bool = params.get("clear", false)
136
100
  var limit: int = params.get("limit", 50)
@@ -0,0 +1,143 @@
1
+ @tool
2
+ extends MCPBaseCommand
3
+ class_name MCPProfilerCommands
4
+
5
+ const PROFILER_TIMEOUT := 5.0
6
+ const GENERIC_TIMEOUT := 5.0
7
+
8
+ var _performance_metrics_pending: bool = false
9
+ var _performance_metrics_result: Dictionary = {}
10
+
11
+
12
+ func get_commands() -> Dictionary:
13
+ return {
14
+ "get_performance_metrics": get_performance_metrics,
15
+ "start_profiler": start_profiler,
16
+ "stop_profiler": stop_profiler,
17
+ "get_profiler_data": get_profiler_data,
18
+ "get_active_processes": get_active_processes,
19
+ "get_signal_connections": get_signal_connections,
20
+ }
21
+
22
+
23
+ func get_performance_metrics(_params: Dictionary) -> Dictionary:
24
+ if not EditorInterface.is_playing_scene():
25
+ return _error("NOT_RUNNING", "No game is currently running")
26
+
27
+ var debugger_plugin = _plugin.get_debugger_plugin() if _plugin else null
28
+ if debugger_plugin == null or not debugger_plugin.has_active_session():
29
+ return _error("NO_SESSION", "No active debug session")
30
+
31
+ _performance_metrics_pending = true
32
+ _performance_metrics_result = {}
33
+
34
+ debugger_plugin.performance_metrics_received.connect(_on_performance_metrics_received, CONNECT_ONE_SHOT)
35
+ debugger_plugin.request_performance_metrics()
36
+
37
+ var start_time := Time.get_ticks_msec()
38
+ while _performance_metrics_pending:
39
+ await Engine.get_main_loop().process_frame
40
+ if (Time.get_ticks_msec() - start_time) / 1000.0 > PROFILER_TIMEOUT:
41
+ _performance_metrics_pending = false
42
+ if debugger_plugin.performance_metrics_received.is_connected(_on_performance_metrics_received):
43
+ debugger_plugin.performance_metrics_received.disconnect(_on_performance_metrics_received)
44
+ return _error("TIMEOUT", "Timed out waiting for performance metrics")
45
+
46
+ return _success(_performance_metrics_result)
47
+
48
+
49
+ func _on_performance_metrics_received(metrics: Dictionary) -> void:
50
+ _performance_metrics_pending = false
51
+ _performance_metrics_result = metrics
52
+
53
+
54
+ func start_profiler(_params: Dictionary) -> Dictionary:
55
+ if not EditorInterface.is_playing_scene():
56
+ return _error("NOT_RUNNING", "No game is currently running")
57
+
58
+ var debugger_plugin = _plugin.get_debugger_plugin() if _plugin else null
59
+ if debugger_plugin == null or not debugger_plugin.has_active_session():
60
+ return _error("NO_SESSION", "No active debug session")
61
+
62
+ debugger_plugin.toggle_frame_profiler(true)
63
+ return _success({"message": "Frame profiler started"})
64
+
65
+
66
+ func stop_profiler(_params: Dictionary) -> Dictionary:
67
+ if not EditorInterface.is_playing_scene():
68
+ return _error("NOT_RUNNING", "No game is currently running")
69
+
70
+ var debugger_plugin = _plugin.get_debugger_plugin() if _plugin else null
71
+ if debugger_plugin == null or not debugger_plugin.has_active_session():
72
+ return _error("NO_SESSION", "No active debug session")
73
+
74
+ debugger_plugin.toggle_frame_profiler(false)
75
+ return _success({"message": "Frame profiler stopped"})
76
+
77
+
78
+ func get_profiler_data(_params: Dictionary) -> Dictionary:
79
+ var result = await _send_and_wait("get_profiler_data")
80
+ if result == null:
81
+ return _last_error
82
+ var result_dict: Dictionary
83
+ if result is Dictionary:
84
+ result_dict = result
85
+ else:
86
+ result_dict = {"data": result}
87
+ return _success(result_dict)
88
+
89
+
90
+ func get_active_processes(_params: Dictionary) -> Dictionary:
91
+ var result = await _send_and_wait("get_active_processes")
92
+ if result == null:
93
+ return _last_error
94
+ var result_dict: Dictionary
95
+ if result is Dictionary:
96
+ result_dict = result
97
+ else:
98
+ result_dict = {"data": result}
99
+ return _success(result_dict)
100
+
101
+
102
+ func get_signal_connections(params: Dictionary) -> Dictionary:
103
+ var node_path: String = params.get("node_path", "")
104
+ var result = await _send_and_wait("get_signal_connections", [node_path])
105
+ if result == null:
106
+ return _last_error
107
+ var result_dict: Dictionary
108
+ if result is Dictionary:
109
+ result_dict = result
110
+ else:
111
+ result_dict = {"data": result}
112
+ return _success(result_dict)
113
+
114
+
115
+ var _last_error: Dictionary = {}
116
+
117
+
118
+ func _send_and_wait(msg_type: String, args: Array = []):
119
+ if not EditorInterface.is_playing_scene():
120
+ _last_error = _error("NOT_RUNNING", "No game is currently running")
121
+ return null
122
+
123
+ var debugger_plugin = _plugin.get_debugger_plugin() if _plugin else null
124
+ if debugger_plugin == null or not debugger_plugin.has_active_session():
125
+ _last_error = _error("NO_SESSION", "No active debug session")
126
+ return null
127
+
128
+ var sent: bool = debugger_plugin.send_game_message(msg_type, args)
129
+ if not sent:
130
+ _last_error = _error("SEND_FAILED", "Failed to send message to game")
131
+ return null
132
+
133
+ var start_time := Time.get_ticks_msec()
134
+ while not debugger_plugin.has_response(msg_type):
135
+ await Engine.get_main_loop().process_frame
136
+ if (Time.get_ticks_msec() - start_time) / 1000.0 > GENERIC_TIMEOUT:
137
+ debugger_plugin.clear_response(msg_type)
138
+ _last_error = _error("TIMEOUT", "Timed out waiting for %s response" % msg_type)
139
+ return null
140
+
141
+ var response = debugger_plugin.get_response(msg_type)
142
+ debugger_plugin.clear_response(msg_type)
143
+ return response
@@ -9,6 +9,7 @@ signal find_nodes_received(matches: Array, count: int, error: String)
9
9
  signal input_map_received(actions: Array, error: String)
10
10
  signal input_sequence_completed(result: Dictionary)
11
11
  signal type_text_completed(result: Dictionary)
12
+ signal game_response(message_type: String, data: Variant)
12
13
 
13
14
  var _active_session_id: int = -1
14
15
  var _pending_screenshot: bool = false
@@ -18,6 +19,8 @@ var _pending_find_nodes: bool = false
18
19
  var _pending_input_map: bool = false
19
20
  var _pending_input_sequence: bool = false
20
21
  var _pending_type_text: bool = false
22
+ var _pending_requests: Dictionary = {}
23
+ var _responses: Dictionary = {}
21
24
 
22
25
 
23
26
  func _has_capture(prefix: String) -> bool:
@@ -47,6 +50,9 @@ func _capture(message: String, data: Array, session_id: int) -> bool:
47
50
  "godot_mcp:type_text_result":
48
51
  _handle_type_text_result(data)
49
52
  return true
53
+ "godot_mcp:game_response":
54
+ _handle_game_response(data)
55
+ return true
50
56
  return false
51
57
 
52
58
 
@@ -77,6 +83,9 @@ func _session_stopped() -> void:
77
83
  if _pending_type_text:
78
84
  _pending_type_text = false
79
85
  type_text_completed.emit({"error": "Game session ended"})
86
+ for msg_type in _pending_requests:
87
+ _responses[msg_type] = {}
88
+ _pending_requests.clear()
80
89
 
81
90
 
82
91
  func has_active_session() -> bool:
@@ -229,3 +238,46 @@ func _handle_type_text_result(data: Array) -> void:
229
238
  _pending_type_text = false
230
239
  var result: Dictionary = data[0] if data.size() > 0 else {}
231
240
  type_text_completed.emit(result)
241
+
242
+
243
+ func send_game_message(msg_type: String, args: Array = []) -> bool:
244
+ if _active_session_id < 0:
245
+ return false
246
+ var session := get_session(_active_session_id)
247
+ if not session:
248
+ return false
249
+ _pending_requests[msg_type] = true
250
+ _responses.erase(msg_type)
251
+ session.send_message("godot_mcp:" + msg_type, args)
252
+ return true
253
+
254
+
255
+ func has_response(msg_type: String) -> bool:
256
+ return _responses.has(msg_type)
257
+
258
+
259
+ func get_response(msg_type: String) -> Variant:
260
+ return _responses.get(msg_type)
261
+
262
+
263
+ func clear_response(msg_type: String) -> void:
264
+ _responses.erase(msg_type)
265
+ _pending_requests.erase(msg_type)
266
+
267
+
268
+ func _handle_game_response(data: Array) -> void:
269
+ if data.size() < 2:
270
+ return
271
+ var msg_type: String = data[0]
272
+ var response_data: Variant = data[1]
273
+ _pending_requests.erase(msg_type)
274
+ _responses[msg_type] = response_data
275
+ game_response.emit(msg_type, response_data)
276
+
277
+
278
+ func toggle_frame_profiler(enable: bool) -> void:
279
+ if _active_session_id < 0:
280
+ return
281
+ var session := get_session(_active_session_id)
282
+ if session:
283
+ session.toggle_profiler("mcp_frame_profiler", enable)
@@ -0,0 +1,61 @@
1
+ extends EngineProfiler
2
+ class_name MCPFrameProfiler
3
+
4
+ const MAX_FRAMES := 300
5
+ const MONITOR_SAMPLE_INTERVAL := 10
6
+
7
+ var _active := false
8
+ var _buffer: Array[Dictionary] = []
9
+ var _frame_index := 0
10
+
11
+
12
+ func _toggle(enable: bool, _options: Array) -> void:
13
+ _active = enable
14
+ if enable:
15
+ _buffer.clear()
16
+ _frame_index = 0
17
+
18
+
19
+ func _tick(frame_time: float, process_time: float, physics_time: float, physics_frame_time: float) -> void:
20
+ if not _active:
21
+ return
22
+
23
+ var entry := {
24
+ "ft": frame_time,
25
+ "pt": process_time,
26
+ "pht": physics_time,
27
+ "pft": physics_frame_time,
28
+ "i": _frame_index,
29
+ }
30
+
31
+ if _frame_index % MONITOR_SAMPLE_INTERVAL == 0:
32
+ entry["m"] = _snapshot_monitors()
33
+
34
+ _buffer.append(entry)
35
+ if _buffer.size() > MAX_FRAMES:
36
+ _buffer.pop_front()
37
+
38
+ _frame_index += 1
39
+
40
+
41
+ func get_buffer_data() -> Dictionary:
42
+ return {
43
+ "active": _active,
44
+ "frame_count": _buffer.size(),
45
+ "total_frames_collected": _frame_index,
46
+ "max_fps": Engine.max_fps,
47
+ "frames": _buffer.duplicate(),
48
+ }
49
+
50
+
51
+ func _snapshot_monitors() -> Dictionary:
52
+ return {
53
+ "fps": Performance.get_monitor(Performance.TIME_FPS),
54
+ "obj_count": int(Performance.get_monitor(Performance.OBJECT_COUNT)),
55
+ "node_count": int(Performance.get_monitor(Performance.OBJECT_NODE_COUNT)),
56
+ "orphan_nodes": int(Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT)),
57
+ "mem_static": int(Performance.get_monitor(Performance.MEMORY_STATIC)),
58
+ "render_objects": int(Performance.get_monitor(Performance.RENDER_TOTAL_OBJECTS_IN_FRAME)),
59
+ "render_draw_calls": int(Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME)),
60
+ "render_primitives": int(Performance.get_monitor(Performance.RENDER_TOTAL_PRIMITIVES_IN_FRAME)),
61
+ }
@@ -4,6 +4,7 @@ class_name MCPGameBridge
4
4
  const DEFAULT_MAX_WIDTH := 1920
5
5
 
6
6
  var _logger: _MCPGameLogger
7
+ var _profiler: MCPFrameProfiler
7
8
 
8
9
 
9
10
  func _ready() -> void:
@@ -11,6 +12,8 @@ func _ready() -> void:
11
12
  return
12
13
  _logger = _MCPGameLogger.new()
13
14
  OS.add_logger(_logger)
15
+ _profiler = MCPFrameProfiler.new()
16
+ EngineDebugger.register_profiler("mcp_frame_profiler", _profiler)
14
17
  EngineDebugger.register_message_capture("godot_mcp", _on_debugger_message)
15
18
  MCPLog.info("Game bridge initialized")
16
19
 
@@ -18,6 +21,8 @@ func _ready() -> void:
18
21
  func _exit_tree() -> void:
19
22
  if EngineDebugger.is_active():
20
23
  EngineDebugger.unregister_message_capture("godot_mcp")
24
+ if _profiler:
25
+ EngineDebugger.unregister_profiler("mcp_frame_profiler")
21
26
 
22
27
 
23
28
  func _process(_delta: float) -> void:
@@ -75,6 +80,15 @@ func _on_debugger_message(message: String, data: Array) -> bool:
75
80
  "type_text":
76
81
  _handle_type_text(data)
77
82
  return true
83
+ "get_profiler_data":
84
+ _handle_get_profiler_data()
85
+ return true
86
+ "get_active_processes":
87
+ _handle_get_active_processes()
88
+ return true
89
+ "get_signal_connections":
90
+ _handle_get_signal_connections(data)
91
+ return true
78
92
  return false
79
93
 
80
94
 
@@ -191,19 +205,175 @@ func _handle_get_performance_metrics() -> void:
191
205
  "render_objects": int(Performance.get_monitor(Performance.RENDER_TOTAL_OBJECTS_IN_FRAME)),
192
206
  "render_draw_calls": int(Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME)),
193
207
  "render_primitives": int(Performance.get_monitor(Performance.RENDER_TOTAL_PRIMITIVES_IN_FRAME)),
208
+ "render_video_mem": int(Performance.get_monitor(Performance.RENDER_VIDEO_MEM_USED)),
209
+ "render_texture_mem": int(Performance.get_monitor(Performance.RENDER_TEXTURE_MEM_USED)),
210
+ "render_buffer_mem": int(Performance.get_monitor(Performance.RENDER_BUFFER_MEM_USED)),
194
211
  "physics_2d_active_objects": int(Performance.get_monitor(Performance.PHYSICS_2D_ACTIVE_OBJECTS)),
195
212
  "physics_2d_collision_pairs": int(Performance.get_monitor(Performance.PHYSICS_2D_COLLISION_PAIRS)),
196
213
  "physics_2d_island_count": int(Performance.get_monitor(Performance.PHYSICS_2D_ISLAND_COUNT)),
214
+ "physics_3d_active_objects": int(Performance.get_monitor(Performance.PHYSICS_3D_ACTIVE_OBJECTS)),
215
+ "physics_3d_collision_pairs": int(Performance.get_monitor(Performance.PHYSICS_3D_COLLISION_PAIRS)),
216
+ "physics_3d_island_count": int(Performance.get_monitor(Performance.PHYSICS_3D_ISLAND_COUNT)),
217
+ "audio_output_latency": Performance.get_monitor(Performance.AUDIO_OUTPUT_LATENCY),
197
218
  "object_count": int(Performance.get_monitor(Performance.OBJECT_COUNT)),
198
219
  "object_resource_count": int(Performance.get_monitor(Performance.OBJECT_RESOURCE_COUNT)),
199
220
  "object_node_count": int(Performance.get_monitor(Performance.OBJECT_NODE_COUNT)),
200
221
  "object_orphan_node_count": int(Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT)),
201
222
  "memory_static": int(Performance.get_monitor(Performance.MEMORY_STATIC)),
202
223
  "memory_static_max": int(Performance.get_monitor(Performance.MEMORY_STATIC_MAX)),
224
+ "memory_msg_buffer_max": int(Performance.get_monitor(Performance.MEMORY_MESSAGE_BUFFER_MAX)),
225
+ "navigation_active_maps": int(Performance.get_monitor(Performance.NAVIGATION_ACTIVE_MAPS)),
226
+ "navigation_region_count": int(Performance.get_monitor(Performance.NAVIGATION_REGION_COUNT)),
227
+ "navigation_agent_count": int(Performance.get_monitor(Performance.NAVIGATION_AGENT_COUNT)),
228
+ "navigation_link_count": int(Performance.get_monitor(Performance.NAVIGATION_LINK_COUNT)),
229
+ "navigation_polygon_count": int(Performance.get_monitor(Performance.NAVIGATION_POLYGON_COUNT)),
230
+ "navigation_edge_count": int(Performance.get_monitor(Performance.NAVIGATION_EDGE_COUNT)),
231
+ "navigation_edge_merge_count": int(Performance.get_monitor(Performance.NAVIGATION_EDGE_MERGE_COUNT)),
232
+ "navigation_edge_connection_count": int(Performance.get_monitor(Performance.NAVIGATION_EDGE_CONNECTION_COUNT)),
233
+ "navigation_edge_free_count": int(Performance.get_monitor(Performance.NAVIGATION_EDGE_FREE_COUNT)),
234
+ "navigation_obstacle_count": int(Performance.get_monitor(Performance.NAVIGATION_OBSTACLE_COUNT)),
235
+ "pipeline_compilations_canvas": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_CANVAS)),
236
+ "pipeline_compilations_mesh": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_MESH)),
237
+ "pipeline_compilations_surface": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_SURFACE)),
238
+ "pipeline_compilations_draw": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_DRAW)),
239
+ "pipeline_compilations_specialization": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_SPECIALIZATION)),
203
240
  }
241
+
242
+ var rid := get_viewport().get_viewport_rid()
243
+ metrics["viewport_render_cpu_ms"] = RenderingServer.viewport_get_measured_render_time_cpu(rid) + RenderingServer.viewport_get_measured_render_time_gpu(rid)
244
+ metrics["viewport_render_gpu_ms"] = RenderingServer.viewport_get_measured_render_time_gpu(rid)
245
+
204
246
  EngineDebugger.send_message("godot_mcp:performance_metrics_result", [metrics])
205
247
 
206
248
 
249
+ func _handle_get_profiler_data() -> void:
250
+ var data := _profiler.get_buffer_data() if _profiler else {}
251
+ EngineDebugger.send_message("godot_mcp:game_response", ["get_profiler_data", data])
252
+
253
+
254
+ func _handle_get_active_processes() -> void:
255
+ var tree := get_tree()
256
+ var scene_root := tree.current_scene if tree else null
257
+ if not scene_root:
258
+ EngineDebugger.send_message("godot_mcp:game_response", ["get_active_processes", {"processes": []}])
259
+ return
260
+
261
+ var script_map: Dictionary = {}
262
+ _collect_processes(scene_root, scene_root, script_map)
263
+
264
+ var processes: Array = []
265
+ for script_path in script_map:
266
+ processes.append(script_map[script_path])
267
+
268
+ processes.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
269
+ return a.instance_count > b.instance_count
270
+ )
271
+
272
+ EngineDebugger.send_message("godot_mcp:game_response", ["get_active_processes", {"processes": processes}])
273
+
274
+
275
+ func _collect_processes(node: Node, scene_root: Node, script_map: Dictionary) -> void:
276
+ var is_proc := node.is_processing()
277
+ var is_phys := node.is_physics_processing()
278
+
279
+ if is_proc or is_phys:
280
+ var script_path := ""
281
+ var script := node.get_script()
282
+ if script and script is Script:
283
+ script_path = script.resource_path
284
+ if script_path.is_empty():
285
+ script_path = node.get_class()
286
+
287
+ if not script_map.has(script_path):
288
+ script_map[script_path] = {
289
+ "script_path": script_path,
290
+ "has_process": false,
291
+ "has_physics_process": false,
292
+ "instance_count": 0,
293
+ "example_paths": [],
294
+ }
295
+
296
+ var entry: Dictionary = script_map[script_path]
297
+ if is_proc:
298
+ entry.has_process = true
299
+ if is_phys:
300
+ entry.has_physics_process = true
301
+ entry.instance_count += 1
302
+ if entry.example_paths.size() < 3:
303
+ var path := "/root/" + scene_root.name
304
+ var relative := scene_root.get_path_to(node)
305
+ if relative != NodePath("."):
306
+ path += "/" + str(relative)
307
+ entry.example_paths.append(path)
308
+
309
+ for child in node.get_children():
310
+ _collect_processes(child, scene_root, script_map)
311
+
312
+
313
+ func _handle_get_signal_connections(data: Array) -> void:
314
+ var node_path: String = data[0] if data.size() > 0 else ""
315
+
316
+ var tree := get_tree()
317
+ var scene_root := tree.current_scene if tree else null
318
+ if not scene_root:
319
+ EngineDebugger.send_message("godot_mcp:game_response", ["get_signal_connections", {"connections": []}])
320
+ return
321
+
322
+ var search_root: Node = scene_root
323
+ if not node_path.is_empty():
324
+ search_root = _get_node_from_path(node_path, scene_root)
325
+ if not search_root:
326
+ EngineDebugger.send_message("godot_mcp:game_response", ["get_signal_connections", {"connections": [], "error": "Node not found: " + node_path}])
327
+ return
328
+
329
+ var connections: Array = []
330
+ _collect_signal_connections(search_root, scene_root, connections, 0)
331
+
332
+ EngineDebugger.send_message("godot_mcp:game_response", ["get_signal_connections", {"connections": connections}])
333
+
334
+
335
+ const MAX_SIGNAL_CONNECTIONS := 200
336
+ const MAX_SIGNAL_DEPTH := 20
337
+
338
+
339
+ func _collect_signal_connections(node: Node, scene_root: Node, connections: Array, depth: int) -> void:
340
+ if connections.size() >= MAX_SIGNAL_CONNECTIONS or depth > MAX_SIGNAL_DEPTH:
341
+ return
342
+
343
+ var source_path := _node_path_string(node, scene_root)
344
+
345
+ for sig_info in node.get_signal_list():
346
+ var sig_name: String = sig_info.name
347
+ for conn in node.get_signal_connection_list(sig_name):
348
+ if connections.size() >= MAX_SIGNAL_CONNECTIONS:
349
+ return
350
+ var target: Object = conn.callable.get_object()
351
+ var target_path := ""
352
+ if target is Node:
353
+ target_path = _node_path_string(target as Node, scene_root)
354
+ else:
355
+ target_path = str(target)
356
+ connections.append({
357
+ "source_path": source_path,
358
+ "signal_name": sig_name,
359
+ "target_path": target_path,
360
+ "method_name": conn.callable.get_method(),
361
+ })
362
+
363
+ for child in node.get_children():
364
+ if connections.size() >= MAX_SIGNAL_CONNECTIONS:
365
+ return
366
+ _collect_signal_connections(child, scene_root, connections, depth + 1)
367
+
368
+
369
+ func _node_path_string(node: Node, scene_root: Node) -> String:
370
+ var path := "/root/" + scene_root.name
371
+ var relative := scene_root.get_path_to(node)
372
+ if relative != NodePath("."):
373
+ path += "/" + str(relative)
374
+ return path
375
+
376
+
207
377
  class _MCPGameLogger extends Logger:
208
378
  var _output: PackedStringArray = []
209
379
  var _max_lines := 1000
package/addon/plugin.cfg CHANGED
@@ -3,6 +3,6 @@
3
3
  name="Godot MCP"
4
4
  description="Model Context Protocol server for AI assistant integration"
5
5
  author="godot-mcp"
6
- version="2.16.1"
6
+ version="2.17.0"
7
7
  script="plugin.gd"
8
8
  godot_version_min="4.5"
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=profiler-actions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler-actions.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/tools/profiler-actions.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,95 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { createMockGodot, createToolContext } from '../helpers/mock-godot.js';
3
+ import { profiler } from '../../tools/profiler.js';
4
+ describe('profiler tool actions', () => {
5
+ let mock;
6
+ beforeEach(() => {
7
+ mock = createMockGodot();
8
+ });
9
+ describe('get_active_processes', () => {
10
+ it('returns message when no processes found', async () => {
11
+ mock.mockResponse({ processes: [] });
12
+ const ctx = createToolContext(mock);
13
+ const result = await profiler.execute({ action: 'get_active_processes' }, ctx);
14
+ expect(result).toBe('No active _process or _physics_process functions found');
15
+ });
16
+ it('formats process list with script paths and instance counts', async () => {
17
+ mock.mockResponse({
18
+ processes: [
19
+ {
20
+ script_path: 'res://enemy.gd',
21
+ has_process: true,
22
+ has_physics_process: true,
23
+ instance_count: 5,
24
+ example_paths: ['/root/Main/Enemy1', '/root/Main/Enemy2'],
25
+ },
26
+ {
27
+ script_path: 'res://player.gd',
28
+ has_process: true,
29
+ has_physics_process: false,
30
+ instance_count: 1,
31
+ example_paths: ['/root/Main/Player'],
32
+ },
33
+ ],
34
+ });
35
+ const ctx = createToolContext(mock);
36
+ const result = await profiler.execute({ action: 'get_active_processes' }, ctx);
37
+ expect(result).toContain('res://enemy.gd');
38
+ expect(result).toContain('_process');
39
+ expect(result).toContain('_physics_process');
40
+ expect(result).toContain('Instances: 5');
41
+ expect(result).toContain('res://player.gd');
42
+ expect(result).toContain('Instances: 1');
43
+ });
44
+ it('sends correct command to Godot', async () => {
45
+ mock.mockResponse({ processes: [] });
46
+ const ctx = createToolContext(mock);
47
+ await profiler.execute({ action: 'get_active_processes' }, ctx);
48
+ expect(mock.calls[0].command).toBe('get_active_processes');
49
+ });
50
+ });
51
+ describe('get_signal_connections', () => {
52
+ it('returns message when no connections found', async () => {
53
+ mock.mockResponse({ connections: [] });
54
+ const ctx = createToolContext(mock);
55
+ const result = await profiler.execute({ action: 'get_signal_connections' }, ctx);
56
+ expect(result).toBe('No signal connections found');
57
+ });
58
+ it('formats signal connection graph', async () => {
59
+ mock.mockResponse({
60
+ connections: [
61
+ {
62
+ source_path: '/root/Main/Button',
63
+ signal_name: 'pressed',
64
+ target_path: '/root/Main/Player',
65
+ method_name: '_on_button_pressed',
66
+ },
67
+ {
68
+ source_path: '/root/Main/Timer',
69
+ signal_name: 'timeout',
70
+ target_path: '/root/Main/Spawner',
71
+ method_name: '_on_timer_timeout',
72
+ },
73
+ ],
74
+ });
75
+ const ctx = createToolContext(mock);
76
+ const result = await profiler.execute({ action: 'get_signal_connections' }, ctx);
77
+ expect(result).toContain('Signal connections (2)');
78
+ expect(result).toContain('/root/Main/Button.pressed -> /root/Main/Player._on_button_pressed');
79
+ expect(result).toContain('/root/Main/Timer.timeout -> /root/Main/Spawner._on_timer_timeout');
80
+ });
81
+ it('passes node_path parameter to Godot', async () => {
82
+ mock.mockResponse({ connections: [] });
83
+ const ctx = createToolContext(mock);
84
+ await profiler.execute({ action: 'get_signal_connections', node_path: '/root/Main/UI' }, ctx);
85
+ expect(mock.calls[0].params.node_path).toBe('/root/Main/UI');
86
+ });
87
+ it('sends empty string when node_path not provided', async () => {
88
+ mock.mockResponse({ connections: [] });
89
+ const ctx = createToolContext(mock);
90
+ await profiler.execute({ action: 'get_signal_connections' }, ctx);
91
+ expect(mock.calls[0].params.node_path).toBe('');
92
+ });
93
+ });
94
+ });
95
+ //# sourceMappingURL=profiler-actions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler-actions.test.js","sourceRoot":"","sources":["../../../src/__tests__/tools/profiler-actions.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=profiler.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/tools/profiler.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,163 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { createMockGodot, createToolContext } from '../helpers/mock-godot.js';
3
+ import { profiler, computePercentiles, detectSpikes, computeMonitorTrends, computeFrameBudget } from '../../tools/profiler.js';
4
+ describe('profiler tool', () => {
5
+ let mock;
6
+ beforeEach(() => {
7
+ mock = createMockGodot();
8
+ });
9
+ describe('schema validation', () => {
10
+ it('accepts valid actions', () => {
11
+ expect(profiler.schema.safeParse({ action: 'snapshot' }).success).toBe(true);
12
+ expect(profiler.schema.safeParse({ action: 'start' }).success).toBe(true);
13
+ expect(profiler.schema.safeParse({ action: 'stop' }).success).toBe(true);
14
+ expect(profiler.schema.safeParse({ action: 'get_data' }).success).toBe(true);
15
+ expect(profiler.schema.safeParse({ action: 'get_active_processes' }).success).toBe(true);
16
+ });
17
+ it('rejects node_path on non-signal actions', () => {
18
+ expect(profiler.schema.safeParse({ action: 'snapshot', node_path: '/root/Test' }).success).toBe(false);
19
+ });
20
+ it('accepts node_path on get_signal_connections', () => {
21
+ expect(profiler.schema.safeParse({ action: 'get_signal_connections', node_path: '/root/Test' }).success).toBe(true);
22
+ expect(profiler.schema.safeParse({ action: 'get_signal_connections' }).success).toBe(true);
23
+ });
24
+ });
25
+ describe('snapshot', () => {
26
+ it('returns JSON with all performance metrics', async () => {
27
+ const metrics = { fps: 60, frame_time_ms: 16.6, memory_static: 1024 };
28
+ mock.mockResponse(metrics);
29
+ const ctx = createToolContext(mock);
30
+ const result = await profiler.execute({ action: 'snapshot' }, ctx);
31
+ expect(JSON.parse(result)).toEqual(metrics);
32
+ expect(mock.calls[0].command).toBe('get_performance_metrics');
33
+ });
34
+ });
35
+ describe('start/stop', () => {
36
+ it('returns confirmation messages', async () => {
37
+ const ctx = createToolContext(mock);
38
+ mock.mockResponse({ message: 'Frame profiler started' });
39
+ expect(await profiler.execute({ action: 'start' }, ctx)).toBe('Frame profiler started');
40
+ mock.mockResponse({ message: 'Frame profiler stopped' });
41
+ expect(await profiler.execute({ action: 'stop' }, ctx)).toBe('Frame profiler stopped');
42
+ });
43
+ });
44
+ describe('get_data', () => {
45
+ it('returns message when no frames collected', async () => {
46
+ mock.mockResponse({ active: false, frame_count: 0, total_frames_collected: 0, frames: [] });
47
+ const ctx = createToolContext(mock);
48
+ const result = await profiler.execute({ action: 'get_data' }, ctx);
49
+ const parsed = JSON.parse(result);
50
+ expect(parsed.frame_count).toBe(0);
51
+ expect(parsed.message).toContain('No frames collected');
52
+ });
53
+ it('computes statistics and spike detection from frame data', async () => {
54
+ const frames = [
55
+ { ft: 0.016, pt: 0.008, pht: 0.004, pft: 0.004, i: 0, m: { fps: 60 } },
56
+ { ft: 0.017, pt: 0.009, pht: 0.004, pft: 0.004, i: 1 },
57
+ { ft: 0.050, pt: 0.030, pht: 0.004, pft: 0.004, i: 2 },
58
+ ];
59
+ mock.mockResponse({ active: true, frame_count: 3, total_frames_collected: 3, frames });
60
+ const ctx = createToolContext(mock);
61
+ const result = await profiler.execute({ action: 'get_data' }, ctx);
62
+ const parsed = JSON.parse(result);
63
+ expect(parsed.statistics.frame_time).toBeDefined();
64
+ expect(parsed.statistics.frame_time.avg_ms).toBeGreaterThan(0);
65
+ expect(parsed.frame_budget).toBeDefined();
66
+ expect(parsed.frame_budget.target_fps).toBeGreaterThan(0);
67
+ expect(parsed.frame_budget.budget_usage_percent).toBeGreaterThan(0);
68
+ expect(parsed.physics_tick_ms).toBeDefined();
69
+ expect(parsed.spikes).toBeDefined();
70
+ expect(parsed.spikes.count).toBe(1);
71
+ expect(parsed.monitor_trends).toBeDefined();
72
+ });
73
+ });
74
+ });
75
+ describe('profiler statistics', () => {
76
+ describe('computePercentiles', () => {
77
+ it('returns zeros for empty array', () => {
78
+ const stats = computePercentiles([]);
79
+ expect(stats.avg_ms).toBe(0);
80
+ expect(stats.min_ms).toBe(0);
81
+ expect(stats.max_ms).toBe(0);
82
+ });
83
+ it('computes correct values for single element', () => {
84
+ const stats = computePercentiles([0.016]);
85
+ expect(stats.avg_ms).toBe(16);
86
+ expect(stats.min_ms).toBe(16);
87
+ expect(stats.max_ms).toBe(16);
88
+ expect(stats.p50_ms).toBe(16);
89
+ });
90
+ it('computes correct percentiles for multiple values', () => {
91
+ const values = [0.010, 0.016, 0.017, 0.020, 0.050];
92
+ const stats = computePercentiles(values);
93
+ expect(stats.avg_ms).toBeGreaterThan(0);
94
+ expect(stats.min_ms).toBe(10);
95
+ expect(stats.max_ms).toBe(50);
96
+ expect(stats.p50_ms).toBe(17);
97
+ });
98
+ });
99
+ describe('detectSpikes', () => {
100
+ it('returns empty array when no spikes', () => {
101
+ const frames = [
102
+ { ft: 0.016, pt: 0.008, pht: 0.004, pft: 0.004, i: 0 },
103
+ { ft: 0.017, pt: 0.009, pht: 0.004, pft: 0.004, i: 1 },
104
+ ];
105
+ expect(detectSpikes(frames, 0.0165)).toEqual([]);
106
+ });
107
+ it('detects frames exceeding 2x median', () => {
108
+ const frames = [
109
+ { ft: 0.016, pt: 0.008, pht: 0.004, pft: 0.004, i: 0 },
110
+ { ft: 0.050, pt: 0.030, pht: 0.004, pft: 0.004, i: 1, m: { fps: 20 } },
111
+ ];
112
+ const spikes = detectSpikes(frames, 0.016);
113
+ expect(spikes.length).toBe(1);
114
+ expect(spikes[0].frame_index).toBe(1);
115
+ expect(spikes[0].monitors).toEqual({ fps: 20 });
116
+ });
117
+ });
118
+ describe('computeFrameBudget', () => {
119
+ it('computes budget stats for 60fps target', () => {
120
+ const frameTime = { avg_ms: 8, min_ms: 7, max_ms: 10, p50_ms: 8, p95_ms: 9, p99_ms: 10 };
121
+ const result = computeFrameBudget(frameTime, 60);
122
+ expect(result.target_fps).toBe(60);
123
+ expect(result.frame_budget_ms).toBeCloseTo(16.7, 1);
124
+ expect(result.actual_fps).toBe(125);
125
+ expect(result.budget_usage_percent).toBeCloseTo(47.9, 0);
126
+ });
127
+ it('handles 30fps target', () => {
128
+ const frameTime = { avg_ms: 30, min_ms: 28, max_ms: 35, p50_ms: 30, p95_ms: 33, p99_ms: 35 };
129
+ const result = computeFrameBudget(frameTime, 30);
130
+ expect(result.target_fps).toBe(30);
131
+ expect(result.frame_budget_ms).toBeCloseTo(33.3, 1);
132
+ expect(result.actual_fps).toBe(33);
133
+ });
134
+ it('handles 120fps target', () => {
135
+ const frameTime = { avg_ms: 7, min_ms: 6, max_ms: 9, p50_ms: 7, p95_ms: 8, p99_ms: 9 };
136
+ const result = computeFrameBudget(frameTime, 120);
137
+ expect(result.target_fps).toBe(120);
138
+ expect(result.frame_budget_ms).toBeCloseTo(8.3, 1);
139
+ });
140
+ });
141
+ describe('computeMonitorTrends', () => {
142
+ it('returns empty for frames without monitors', () => {
143
+ const frames = [{ ft: 0.016, pt: 0.008, pht: 0.004, pft: 0.004, i: 0 }];
144
+ expect(computeMonitorTrends(frames)).toEqual({});
145
+ });
146
+ it('computes start/end/avg/max/change_percent for monitor values', () => {
147
+ const frames = [
148
+ { ft: 0.016, pt: 0.008, pht: 0.004, pft: 0.004, i: 0, m: { fps: 60, node_count: 100 } },
149
+ { ft: 0.016, pt: 0.008, pht: 0.004, pft: 0.004, i: 10, m: { fps: 30, node_count: 200 } },
150
+ ];
151
+ const trends = computeMonitorTrends(frames);
152
+ expect(trends.fps.start).toBe(60);
153
+ expect(trends.fps.end).toBe(30);
154
+ expect(trends.fps.avg).toBe(45);
155
+ expect(trends.fps.max).toBe(60);
156
+ expect(trends.fps.change_percent).toBe(-50);
157
+ expect(trends.node_count.start).toBe(100);
158
+ expect(trends.node_count.end).toBe(200);
159
+ expect(trends.node_count.change_percent).toBe(100);
160
+ });
161
+ });
162
+ });
163
+ //# sourceMappingURL=profiler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler.test.js","sourceRoot":"","sources":["../../../src/__tests__/tools/profiler.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/tools/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAyB,MAAM,kBAAkB,CAAC;AAmHjF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0JjB,CAAC;AAEH,eAAO,MAAM,WAAW,EAAe,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/tools/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAyB,MAAM,kBAAkB,CAAC;AAmHjF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0IjB,CAAC;AAEH,eAAO,MAAM,WAAW,EAAe,iBAAiB,EAAE,CAAC"}
@@ -135,8 +135,9 @@ export const editor = defineTool({
135
135
  return JSON.stringify(result, null, 2);
136
136
  }
137
137
  case 'get_performance': {
138
+ const deprecation = '[DEPRECATED] get_performance is deprecated. Use profiler > snapshot instead for all 59 monitors + render timing.\n\n';
138
139
  const result = await godot.sendCommand('get_performance_metrics');
139
- return JSON.stringify(result, null, 2);
140
+ return deprecation + JSON.stringify(result, null, 2);
140
141
  }
141
142
  case 'screenshot_game': {
142
143
  const result = await godot.sendCommand('capture_game_screenshot', { max_width: args.max_width });
@@ -1 +1 @@
1
- {"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/tools/editor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/tools/editor.ts"],"names":[],"mappings":"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"}
@@ -9,4 +9,5 @@ export { resourceTools } from './resource.js';
9
9
  export { scene3dTools } from './scene3d.js';
10
10
  export { docsTools } from './docs.js';
11
11
  export { inputTools } from './input.js';
12
+ export { profilerTools } from './profiler.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAYA,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAaA,wBAAgB,gBAAgB,IAAI,IAAI,CAYvC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -9,6 +9,7 @@ import { resourceTools } from './resource.js';
9
9
  import { scene3dTools } from './scene3d.js';
10
10
  import { docsTools } from './docs.js';
11
11
  import { inputTools } from './input.js';
12
+ import { profilerTools } from './profiler.js';
12
13
  export function registerAllTools() {
13
14
  registry.registerTools(sceneTools);
14
15
  registry.registerTools(nodeTools);
@@ -20,6 +21,7 @@ export function registerAllTools() {
20
21
  registry.registerTools(scene3dTools);
21
22
  registry.registerTools(docsTools);
22
23
  registry.registerTools(inputTools);
24
+ registry.registerTools(profilerTools);
23
25
  }
24
26
  export { sceneTools } from './scene.js';
25
27
  export { nodeTools } from './node.js';
@@ -31,4 +33,5 @@ export { resourceTools } from './resource.js';
31
33
  export { scene3dTools } from './scene3d.js';
32
34
  export { docsTools } from './docs.js';
33
35
  export { inputTools } from './input.js';
36
+ export { profilerTools } from './profiler.js';
34
37
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,UAAU,gBAAgB;IAC9B,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IACpC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;IACvC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;AACrC,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,UAAU,gBAAgB;IAC9B,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IACpC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;IACvC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;AACxC,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod';
2
+ import type { AnyToolDefinition } from '../core/types.js';
3
+ interface FrameEntry {
4
+ ft: number;
5
+ pt: number;
6
+ pht: number;
7
+ pft: number;
8
+ i: number;
9
+ m?: Record<string, number>;
10
+ }
11
+ export interface PercentileStats {
12
+ avg_ms: number;
13
+ min_ms: number;
14
+ max_ms: number;
15
+ p50_ms: number;
16
+ p95_ms: number;
17
+ p99_ms: number;
18
+ }
19
+ export declare function computePercentiles(values: number[]): PercentileStats;
20
+ export interface SpikeInfo {
21
+ frame_index: number;
22
+ frame_time_ms: number;
23
+ monitors?: Record<string, number>;
24
+ }
25
+ export declare function detectSpikes(frames: FrameEntry[], medianFrameTime: number): SpikeInfo[];
26
+ export interface MonitorTrend {
27
+ start: number;
28
+ end: number;
29
+ avg: number;
30
+ max: number;
31
+ change_percent: number;
32
+ }
33
+ export declare function computeMonitorTrends(frames: FrameEntry[]): Record<string, MonitorTrend>;
34
+ export interface FrameBudget {
35
+ target_fps: number;
36
+ actual_fps: number;
37
+ frame_budget_ms: number;
38
+ budget_usage_percent: number;
39
+ }
40
+ export declare function computeFrameBudget(frameTimeStats: PercentileStats, targetFps: number): FrameBudget;
41
+ export declare const profiler: import("../core/types.js").ToolDefinition<z.ZodObject<{
42
+ action: z.ZodEnum<{
43
+ stop: "stop";
44
+ snapshot: "snapshot";
45
+ start: "start";
46
+ get_data: "get_data";
47
+ get_active_processes: "get_active_processes";
48
+ get_signal_connections: "get_signal_connections";
49
+ }>;
50
+ node_path: z.ZodOptional<z.ZodString>;
51
+ }, z.core.$strip>>;
52
+ export declare const profilerTools: AnyToolDefinition[];
53
+ export {};
54
+ //# sourceMappingURL=profiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler.d.ts","sourceRoot":"","sources":["../../src/tools/profiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5B;AAyBD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,CAgBpE;AAUD,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,eAAe,EAAE,MAAM,GAAG,SAAS,EAAE,CAkBvF;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAgCvF;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,eAAe,EAC/B,SAAS,EAAE,MAAM,GAChB,WAAW,CAWb;AA+BD,eAAO,MAAM,QAAQ;;;;;;;;;;kBAuHnB,CAAC;AAEH,eAAO,MAAM,aAAa,EAAiB,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,204 @@
1
+ import { z } from 'zod';
2
+ import { defineTool } from '../core/define-tool.js';
3
+ function toMs(seconds) {
4
+ return Math.round(seconds * 100000) / 100;
5
+ }
6
+ export function computePercentiles(values) {
7
+ if (values.length === 0) {
8
+ return { avg_ms: 0, min_ms: 0, max_ms: 0, p50_ms: 0, p95_ms: 0, p99_ms: 0 };
9
+ }
10
+ const sorted = [...values].sort((a, b) => a - b);
11
+ const sum = sorted.reduce((a, b) => a + b, 0);
12
+ return {
13
+ avg_ms: toMs(sum / sorted.length),
14
+ min_ms: toMs(sorted[0]),
15
+ max_ms: toMs(sorted[sorted.length - 1]),
16
+ p50_ms: toMs(percentile(sorted, 50)),
17
+ p95_ms: toMs(percentile(sorted, 95)),
18
+ p99_ms: toMs(percentile(sorted, 99)),
19
+ };
20
+ }
21
+ function percentile(sorted, p) {
22
+ const index = (p / 100) * (sorted.length - 1);
23
+ const lower = Math.floor(index);
24
+ const upper = Math.ceil(index);
25
+ if (lower === upper)
26
+ return sorted[lower];
27
+ return sorted[lower] + (sorted[upper] - sorted[lower]) * (index - lower);
28
+ }
29
+ export function detectSpikes(frames, medianFrameTime) {
30
+ const threshold = medianFrameTime * 2;
31
+ const spikes = [];
32
+ for (const frame of frames) {
33
+ if (frame.ft > threshold) {
34
+ const spike = {
35
+ frame_index: frame.i,
36
+ frame_time_ms: toMs(frame.ft),
37
+ };
38
+ if (frame.m) {
39
+ spike.monitors = frame.m;
40
+ }
41
+ spikes.push(spike);
42
+ }
43
+ }
44
+ return spikes;
45
+ }
46
+ export function computeMonitorTrends(frames) {
47
+ const monitorFrames = frames.filter((f) => f.m);
48
+ if (monitorFrames.length === 0)
49
+ return {};
50
+ const allKeys = new Set();
51
+ for (const f of monitorFrames) {
52
+ for (const key of Object.keys(f.m)) {
53
+ allKeys.add(key);
54
+ }
55
+ }
56
+ const trends = {};
57
+ for (const key of allKeys) {
58
+ const values = monitorFrames.filter((f) => f.m[key] !== undefined).map((f) => f.m[key]);
59
+ if (values.length === 0)
60
+ continue;
61
+ const sum = values.reduce((a, b) => a + b, 0);
62
+ const start = values[0];
63
+ const end = values[values.length - 1];
64
+ const changePct = start === 0 ? (end === 0 ? 0 : 100) : ((end - start) / start) * 100;
65
+ trends[key] = {
66
+ start,
67
+ end,
68
+ avg: sum / values.length,
69
+ max: Math.max(...values),
70
+ change_percent: Math.round(changePct * 10) / 10,
71
+ };
72
+ }
73
+ return trends;
74
+ }
75
+ export function computeFrameBudget(frameTimeStats, targetFps) {
76
+ const budgetMs = 1000 / targetFps;
77
+ const actualFps = frameTimeStats.avg_ms > 0 ? Math.round(1000 / frameTimeStats.avg_ms) : 0;
78
+ const budgetUsage = Math.round((frameTimeStats.avg_ms / budgetMs) * 1000) / 10;
79
+ return {
80
+ target_fps: targetFps,
81
+ actual_fps: actualFps,
82
+ frame_budget_ms: Math.round(budgetMs * 10) / 10,
83
+ budget_usage_percent: budgetUsage,
84
+ };
85
+ }
86
+ const ProfilerSchema = z
87
+ .object({
88
+ action: z
89
+ .enum([
90
+ 'snapshot',
91
+ 'start',
92
+ 'stop',
93
+ 'get_data',
94
+ 'get_active_processes',
95
+ 'get_signal_connections',
96
+ ])
97
+ .describe('Action: snapshot (full perf snapshot), start/stop/get_data (time series profiling), get_active_processes, get_signal_connections'),
98
+ node_path: z
99
+ .string()
100
+ .optional()
101
+ .describe('Node path for get_signal_connections (optional, defaults to scene root)'),
102
+ })
103
+ .refine((data) => {
104
+ if (data.node_path !== undefined && data.action !== 'get_signal_connections') {
105
+ return false;
106
+ }
107
+ return true;
108
+ }, { message: 'node_path is only valid with get_signal_connections action' });
109
+ export const profiler = defineTool({
110
+ name: 'profiler',
111
+ description: 'Performance profiling and analysis: snapshot all engine metrics, collect per-frame time series data with spike detection, list active _process/_physics_process scripts, inspect signal connections',
112
+ schema: ProfilerSchema,
113
+ async execute(args, { godot }) {
114
+ switch (args.action) {
115
+ case 'snapshot': {
116
+ const result = await godot.sendCommand('get_performance_metrics');
117
+ return JSON.stringify(result, null, 2);
118
+ }
119
+ case 'start': {
120
+ const result = await godot.sendCommand('start_profiler');
121
+ return result.message;
122
+ }
123
+ case 'stop': {
124
+ const result = await godot.sendCommand('stop_profiler');
125
+ return result.message;
126
+ }
127
+ case 'get_data': {
128
+ const result = await godot.sendCommand('get_profiler_data');
129
+ const { frames } = result;
130
+ if (frames.length === 0) {
131
+ return JSON.stringify({
132
+ active: result.active,
133
+ frame_count: 0,
134
+ message: 'No frames collected. Start the profiler first with action: start',
135
+ }, null, 2);
136
+ }
137
+ const frameTimeStats = computePercentiles(frames.map((f) => f.ft));
138
+ const processTimeStats = computePercentiles(frames.map((f) => f.pt));
139
+ const physicsTimeStats = computePercentiles(frames.map((f) => f.pht));
140
+ const spikes = detectSpikes(frames, frameTimeStats.p50_ms / 1000);
141
+ const monitorTrends = computeMonitorTrends(frames);
142
+ const physicsTickMs = frames.length > 0 ? toMs(frames[0].pft) : 16.67;
143
+ const maxFps = result.max_fps || 0;
144
+ const targetFps = maxFps > 0 ? maxFps : Math.round(1000 / physicsTickMs);
145
+ const frameBudget = computeFrameBudget(frameTimeStats, targetFps);
146
+ return JSON.stringify({
147
+ active: result.active,
148
+ frame_count: result.frame_count,
149
+ total_frames_collected: result.total_frames_collected,
150
+ frame_budget: frameBudget,
151
+ statistics: {
152
+ frame_time: frameTimeStats,
153
+ process_time: processTimeStats,
154
+ physics_time: physicsTimeStats,
155
+ },
156
+ physics_tick_ms: physicsTickMs,
157
+ spikes: {
158
+ count: spikes.length,
159
+ threshold: `>${Math.round(frameTimeStats.p50_ms * 2 * 10) / 10}ms (2x median)`,
160
+ frames: spikes.slice(0, 20),
161
+ },
162
+ monitor_trends: monitorTrends,
163
+ }, null, 2);
164
+ }
165
+ case 'get_active_processes': {
166
+ const result = await godot.sendCommand('get_active_processes');
167
+ const { processes } = result;
168
+ if (processes.length === 0) {
169
+ return 'No active _process or _physics_process functions found';
170
+ }
171
+ const lines = [`Active processing scripts (${processes.length} scripts):\n`];
172
+ for (const entry of processes) {
173
+ const funcs = [];
174
+ if (entry.has_process)
175
+ funcs.push('_process');
176
+ if (entry.has_physics_process)
177
+ funcs.push('_physics_process');
178
+ lines.push(` ${entry.script_path}`);
179
+ lines.push(` Functions: ${funcs.join(', ')}`);
180
+ lines.push(` Instances: ${entry.instance_count}`);
181
+ if (entry.example_paths.length > 0) {
182
+ lines.push(` Examples: ${entry.example_paths.join(', ')}`);
183
+ }
184
+ lines.push('');
185
+ }
186
+ return lines.join('\n');
187
+ }
188
+ case 'get_signal_connections': {
189
+ const result = await godot.sendCommand('get_signal_connections', { node_path: args.node_path ?? '' });
190
+ const { connections } = result;
191
+ if (connections.length === 0) {
192
+ return 'No signal connections found';
193
+ }
194
+ const lines = [`Signal connections (${connections.length}):\n`];
195
+ for (const conn of connections) {
196
+ lines.push(` ${conn.source_path}.${conn.signal_name} -> ${conn.target_path}.${conn.method_name}`);
197
+ }
198
+ return lines.join('\n');
199
+ }
200
+ }
201
+ },
202
+ });
203
+ export const profilerTools = [profiler];
204
+ //# sourceMappingURL=profiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler.js","sourceRoot":"","sources":["../../src/tools/profiler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@satelliteoflove/godot-mcp",
3
- "version": "2.16.1",
3
+ "version": "2.17.0",
4
4
  "description": "MCP server for Godot Engine integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",