Module: PostHog::MCP::Exceptions Private

Defined in:
lib/posthog/mcp/exceptions.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Builds PostHog error-tracking properties ($exception_list / $exception_level) from anything a tool can fail with, reusing ExceptionCapture so MCP failures group like every other exception the Ruby SDK reports.

Constant Summary collapse

GENERIC_MECHANISM =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

{ 'type' => 'generic', 'handled' => true }.freeze
DISPATCH_WRAPPER_TYPE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Messages the Ruby mcp gem wraps around whatever a tool raised.

'MCP::Server::RequestHandlerError'
DISPATCH_WRAPPER_PREFIXES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

['Internal error calling tool', 'Internal error handling'].freeze

Class Method Summary collapse

Class Method Details

.call_tool_result?(value) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


73
74
75
76
77
78
# File 'lib/posthog/mcp/exceptions.rb', line 73

def call_tool_result?(value)
  return false unless value.is_a?(Hash)

  content = value['content'] || value[:content]
  (value.key?('isError') || value.key?(:isError)) && content.is_a?(Array)
end

.call_tool_result_message(result) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/posthog/mcp/exceptions.rb', line 80

def call_tool_result_message(result)
  content = result['content'] || result[:content] || []
  texts = content.filter_map do |part|
    next unless part.is_a?(Hash)

    type = part['type'] || part[:type]
    text = part['text'] || part[:text]
    text if type == 'text' && text.is_a?(String)
  end
  joined = texts.join(' ').strip
  joined.empty? ? 'Unknown error' : joined
end

.capture_exception(error) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns {'$exception_list' => [...], '$exception_level' => 'error'}.

Parameters:

  • error (Exception, String, Hash, Object) —

    exception, message, or an isError tool result ({content: [...], isError: true})

Returns:

  • (Hash) —

    {'$exception_list' => [...], '$exception_level' => 'error'}



25
26
27
28
29
30
31
# File 'lib/posthog/mcp/exceptions.rb', line 25

def capture_exception(error)
  return from_message(call_tool_result_message(error)) if call_tool_result?(error)
  return from_exception(error) if error.is_a?(Exception)
  return from_message(error) if error.is_a?(String)

  from_message(safe_to_s(error))
end

.chain_includes?(error, target) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


93
94
95
96
97
98
99
100
101
102
103
# File 'lib/posthog/mcp/exceptions.rb', line 93

def chain_includes?(error, target)
  current = error.cause
  seen = {}.compare_by_identity
  while current && !seen.key?(current)
    return true if current.equal?(target)

    seen[current] = true
    current = current.cause
  end
  false
end

.dispatch_wrapper?(entry) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Whether an $exception_list entry is the gem's dispatch wrapper, whose message says nothing the tool name does not already say.

Returns:

  • (Boolean)


53
54
55
56
57
58
# File 'lib/posthog/mcp/exceptions.rb', line 53

def dispatch_wrapper?(entry)
  return false unless entry.is_a?(Hash)

  value = entry['value'].to_s
  entry['type'] == DISPATCH_WRAPPER_TYPE && DISPATCH_WRAPPER_PREFIXES.any? { |p| value.start_with?(p) }
end

.from_exception(error) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



33
34
35
36
37
38
39
40
41
# File 'lib/posthog/mcp/exceptions.rb', line 33

def from_exception(error)
  list = PostHog::ExceptionCapture.build_exception_list(error) || []
  original = error.respond_to?(:original_error) ? error.original_error : nil
  if original.is_a?(Exception) && !chain_includes?(error, original)
    list.concat(PostHog::ExceptionCapture.build_exception_list(original) || [])
  end
  list = [message_entry(error.class.to_s, error.message.to_s)] if list.empty?
  { '$exception_list' => list, '$exception_level' => 'error' }
end

.from_message(message) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



43
44
45
# File 'lib/posthog/mcp/exceptions.rb', line 43

def from_message(message)
  { '$exception_list' => [message_entry('Error', message)], '$exception_level' => 'error' }
end

.message_entry(type, message) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



47
48
49
# File 'lib/posthog/mcp/exceptions.rb', line 47

def message_entry(type, message)
  { 'mechanism' => GENERIC_MECHANISM.dup, 'type' => type, 'value' => message }
end

.primary_exception(error) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The $exception_list entry carrying the actual failure reason, stepping past consecutive dispatch wrappers.



62
63
64
65
66
67
68
69
70
71
# File 'lib/posthog/mcp/exceptions.rb', line 62

def primary_exception(error)
  return {} unless error.is_a?(Hash)

  list = error['$exception_list']
  return {} unless list.is_a?(Array) && !list.empty?

  index = 0
  index += 1 while index + 1 < list.length && list[index + 1].is_a?(Hash) && dispatch_wrapper?(list[index])
  list[index].is_a?(Hash) ? list[index] : {}
end

.safe_to_s(value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



105
106
107
108
109
# File 'lib/posthog/mcp/exceptions.rb', line 105

def safe_to_s(value)
  value.to_s
rescue StandardError
  'Unknown error'
end