Class: DebugMcp::Tools::RailsRecentEvents

Inherits:
MCP::Tool
  • Object
show all
Defined in:
lib/debug_mcp/tools/rails_recent_events.rb

Overview

Read recent Rails internal events (SQL, render, cache, job enqueue, request lifecycle) from the in-process Notifications buffer, independent of trigger_request.

IMPORTANT semantics, surfaced in every response so the model does not mistake “empty” for “nothing happened”:

  • forward-only: only events fired AFTER the subscriber was installed are visible. The first call installs the subscriber, so it usually returns little or nothing — that is not evidence that no SQL/jobs ran.

  • paused-only: the target must be paused at a debugger prompt (we send commands over the debug socket).

  • NOT read-only: installing the subscriber subscribes to ActiveSupport::Notifications inside the target — an in-process instrumentation side effect (no application data is written).

Constant Summary collapse

DEFAULT_LIMIT =
50
MAX_LIMIT =
500
KIND_MATCHERS =
{
  "sql" => ->(name) { name == "sql.active_record" },
  "render" => ->(name) { name.start_with?("render_") },
  "cache" => ->(name) { name.start_with?("cache_") },
  "job" => ->(name) { name == "enqueue.active_job" },
  "request" => ->(name) { name.end_with?(".action_controller") },
}.freeze

Class Method Summary collapse

Class Method Details

.call(kinds: nil, limit: nil, after_seq: nil, include_debug_eval: false, session_id: nil, server_context:) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/debug_mcp/tools/rails_recent_events.rb', line 82

def call(kinds: nil, limit: nil, after_seq: nil, include_debug_eval: false, session_id: nil,
         server_context:)
  client = server_context[:session_manager].client(session_id)
  client.auto_repause!
  RailsHelper.require_rails!(client)

  # Reads and install both send commands over the debug socket and both
  # take the buffer/Notifications mutex, which fails in trap context.
  if RailsHelper.trap_context?(client)
    return text_response("Rails recent events: unavailable in trap context.\n\n" \
                         "#{RailsHelper::TRAP_CONTEXT_HINT}")
  end

  installed = NotificationsSubscriber.install(client)
  unless installed
    return text_response("Rails recent events: could not install the Notifications subscriber " \
                         "in the target process.\n\n#{RailsHelper::TRAP_CONTEXT_HINT}")
  end

  limit = resolve_limit(limit)
  events = fetch_events(client, after_seq: after_seq, limit: limit)
  events = filter_by_kinds(events, kinds)
   = NotificationsSubscriber.(client)

  # `installed` is the result of the install we just performed — the only
  # trustworthy signal here. metadata[:installed] can be missing if the
  # metadata round-trip itself returned no parseable object.
  text_response(build_output(events, , kinds, include_debug_eval, limit, installed))
rescue DebugMcp::Error => e
  text_response("Error: #{e.message}")
end