Module: RailsAiContext::Tools::SafeCall
- Defined in:
- lib/rails_ai_context/tools/safe_call.rb
Overview
Last-resort rescue for tool execution, prepended to every registered
tool's singleton class so it wraps each tool's self.call regardless
of when the subclass defines the method.
Without this, an exception inside a tool escapes to the MCP SDK, which converts it into a JSON-RPC -32603 protocol error. Most AI clients swallow protocol errors, so the model never sees what went wrong and cannot recover. An error tool-result (isError: true) lands in the model's context instead, with enough detail to retry or route around the failure.
Instance Method Summary collapse
Instance Method Details
#call(**kwargs) ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/rails_ai_context/tools/safe_call.rb', line 16 def call(**kwargs) # Held as a local, not a thread-local: the wrapper sees both the value # it discarded and the response that came back, so the note needs no # state outliving the call. It also lands on every way out of a tool, # not just text_response, and a tool that calls another tool cannot # have its note consumed by the inner one. discarded = discarded_detail(kwargs) kwargs = normalize_detail(kwargs) Thread.current[:rails_ai_context_call_params] = session_params(kwargs) # Held across the tool body: a concurrent live reload must not unload # constants while this call is reading them. # # The rescue sits INSIDE the block on purpose. `executor.wrap` reports # anything that crosses it to the host app's error_reporter as # unhandled, which would page a team over a failure this method turns # into an ordinary isError result. Nothing escapes the block, so # nothing is reported. RailsAiContext::CodeReloader.with_app_code do begin append_note(super(**kwargs), invalid_detail_note(discarded)) rescue StandardError => e # A failed call must not leak its recorded params into the next # call's session entry. Thread.current[:rails_ai_context_call_params] = nil failure_response(e) end end end |