Class: Hitch::MCP::Internal::Observation

Inherits:
Object
  • Object
show all
Defined in:
app/models/hitch/mcp/internal/observation.rb

Overview

Request-local structural telemetry. The state never crosses the public Context or SDK server_context boundary, and publication temporarily detaches it so hostile subscribers cannot recursively observe their own work as part of the request.

Class Method Summary collapse

Class Method Details

.current_request_idObject



246
247
248
249
250
# File 'app/models/hitch/mcp/internal/observation.rb', line 246

def current_request_id
  correlation_id(current_request&.request_id)
rescue StandardError, SystemStackError
  nil
end

.identity_keys(principal:, client_id:, key_generator: Rails.application.key_generator) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'app/models/hitch/mcp/internal/observation.rb', line 267

def identity_keys(principal:, client_id:, key_generator: Rails.application.key_generator)
  record_class = principal.class.respond_to?(:base_class) ? principal.class.base_class : principal.class
  principal_type = record_class.name
  unless principal_type.is_a?(String) && PRINCIPAL_TYPE_PATTERN.match?(principal_type)
    raise ArgumentError, "MCP observation principal type is invalid"
  end

  invalid_message = "MCP observation identity is invalid"
  principal_id = HmacIdentity.component(principal.id.to_s, invalid_message:)
  client = HmacIdentity.component(client_id, invalid_message:)
  principal_key = identity_digest([ "principal", principal_type, principal_id ], key_generator)
  client_key = identity_digest([ "client", client ], key_generator)
  [ principal_type.dup.freeze, principal_key, client_key ].freeze
end

.publish(event_name, payload) ⇒ Object



252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'app/models/hitch/mcp/internal/observation.rb', line 252

def publish(event_name, payload)
  previous = ActiveSupport::IsolatedExecutionState[CURRENT_REQUEST_KEY]
  ActiveSupport::IsolatedExecutionState.delete(CURRENT_REQUEST_KEY)
  ActiveSupport::Notifications.instrument(event_name, payload)
rescue StandardError, SystemStackError
  report_failure(event_name, "subscriber")
  nil
ensure
  if previous
    ActiveSupport::IsolatedExecutionState[CURRENT_REQUEST_KEY] = previous
  else
    ActiveSupport::IsolatedExecutionState.delete(CURRENT_REQUEST_KEY)
  end
end

.report_failure(event_name, category) ⇒ Object



290
291
292
293
294
295
296
297
298
299
300
301
# File 'app/models/hitch/mcp/internal/observation.rb', line 290

def report_failure(event_name, category)
  SanitizedReport.emit(
    source: "hitch.mcp.observation",
    message: "Hitch MCP observation delivery failed",
    context: {
      hitch_mcp_category: "observation_#{category}".freeze,
      hitch_mcp_event: event_name.dup.freeze
    }.freeze
  )
rescue StandardError, SystemStackError
  nil
end

.response_bytes(response) ⇒ Object



282
283
284
285
286
287
288
# File 'app/models/hitch/mcp/internal/observation.rb', line 282

def response_bytes(response)
  body = response.body
  return body.bytesize if body.is_a?(String)
  return 0 if body.nil?

  Array(body).sum { |part| part.to_s.bytesize }
end

.start_invocation(tool_name:) ⇒ Object



239
240
241
242
243
244
# File 'app/models/hitch/mcp/internal/observation.rb', line 239

def start_invocation(tool_name:)
  current_request&.start_invocation(tool_name:)
rescue StandardError, SystemStackError
  report_failure(INVOCATION_EVENT, "invocation_start")
  nil
end

.with_request_stateObject



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'app/models/hitch/mcp/internal/observation.rb', line 216

def with_request_state
  previous = ActiveSupport::IsolatedExecutionState[CURRENT_REQUEST_KEY]
  state = begin
    created = RequestState.new
    ActiveSupport::IsolatedExecutionState[CURRENT_REQUEST_KEY] = created
    created
  rescue StandardError, SystemStackError
    report_failure(REQUEST_EVENT, "request_start")
    nil
  end
  yield state
ensure
  begin
    if previous
      ActiveSupport::IsolatedExecutionState[CURRENT_REQUEST_KEY] = previous
    else
      ActiveSupport::IsolatedExecutionState.delete(CURRENT_REQUEST_KEY)
    end
  rescue StandardError, SystemStackError
    report_failure(REQUEST_EVENT, "request_cleanup")
  end
end