Class: PostHog::MCP::Client
- Inherits:
-
Client
- Object
- Client
- PostHog::MCP::Client
- Defined in:
- lib/posthog/mcp/client.rb
Overview
Experimental.
A Client with first-class MCP analytics for custom dispatchers
(your own HTTP layer, no MCP::Server to wrap). The host resolves
identity and context per request and calls the capture methods directly;
events flow through the same sanitize -> truncate -> $exception fan-out
pipeline as instrument. Does not need the mcp gem.
Instance Method Summary collapse
-
#capture_initialize(client_name: nil, client_version: nil, protocol_version: nil, parameters: nil, response: nil, duration_ms: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil) ⇒ void
Capture the connection handshake.
-
#capture_missing_capability(context: nil, parameters: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil, llm_model: nil, llm_model_source: nil) ⇒ void
Capture a
get_more_toolscall as a missing-capability report. -
#capture_tool_call(tool_name, intent: nil, intent_source: nil, parameters: nil, response: nil, duration_ms: nil, is_error: false, error: nil, error_type: nil, category: nil, tool_description: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil, llm_model: nil, llm_model_source: nil) ⇒ void
Capture a tool invocation.
-
#capture_tools_list(tool_names: nil, parameters: nil, response: nil, duration_ms: nil, is_error: false, error: nil, error_type: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil) ⇒ void
Capture a
tools/listresponse. -
#initialize(opts = {}) ⇒ Client
constructor
A new instance of Client.
-
#prepare_tool_call(name, args = nil, input_schema: nil) ⇒ PreparedToolCall
Pull the agent's intent off the
contextargument and its self-reported model offllm_model, strip the arguments this integration injected, and flag theget_more_toolsvirtual tool. -
#prepare_tool_list(tools, context: true, report_missing: false, capture_model: @capture_model_option) ⇒ Array<Hash>
Inject the
contextandllm_modelarguments into every tool descriptor (Hash withinputSchema) so agents state their intent, and optionally append theget_more_toolsvirtual tool.
Constructor Details
#initialize(opts = {}) ⇒ Client
Returns a new instance of Client.
21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/posthog/mcp/client.rb', line 21 def initialize(opts = {}) opts = opts.transform_keys(&:to_sym) @missing_capability_tool_name = opts.delete(:missing_capability_tool_name) || Tools::GET_MORE_TOOLS_NAME @mcp_exception_autocapture = opts.delete(:mcp_exception_autocapture) != false capture_model = opts.key?(:capture_model) ? opts.delete(:capture_model) : true @capture_model_option = Options.new(capture_model: capture_model).capture_model super @mcp_sink = Sink.new(self) @mcp_options = Options.new( enable_exception_autocapture: @mcp_exception_autocapture, missing_capability_tool_name: @missing_capability_tool_name ) end |
Instance Method Details
#capture_initialize(client_name: nil, client_version: nil, protocol_version: nil, parameters: nil, response: nil, duration_ms: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil) ⇒ void
This method returns an undefined value.
Capture the connection handshake. Emits $mcp_initialize.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/posthog/mcp/client.rb', line 66 def capture_initialize(client_name: nil, client_version: nil, protocol_version: nil, parameters: nil, response: nil, duration_ms: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil) event = base_event(EventType::MCP_INITIALIZE, distinct_id, session_id, set_properties, groups, properties, , client_user_agent, vendor_client) event['client_name'] = client_name event['client_version'] = client_version event['protocol_version'] = protocol_version event['parameters'] = parameters event['response'] = response event['duration'] = duration_ms emit(event) end |
#capture_missing_capability(context: nil, parameters: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil, llm_model: nil, llm_model_source: nil) ⇒ void
This method returns an undefined value.
Capture a get_more_tools call as a missing-capability report. Emits
$mcp_missing_capability with the agent's description as $mcp_intent.
105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/posthog/mcp/client.rb', line 105 def capture_missing_capability(context: nil, parameters: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil, llm_model: nil, llm_model_source: nil) event = base_event(EventType::MCP_MISSING_CAPABILITY, distinct_id, session_id, set_properties, groups, properties, , client_user_agent, vendor_client) event['resource_name'] = @missing_capability_tool_name event['protocol_version'] = protocol_version event['parameters'] = parameters apply_intent(event, context, 'context_parameter') apply_model(event, llm_model, llm_model_source) emit(event) end |
#capture_tool_call(tool_name, intent: nil, intent_source: nil, parameters: nil, response: nil, duration_ms: nil, is_error: false, error: nil, error_type: nil, category: nil, tool_description: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil, llm_model: nil, llm_model_source: nil) ⇒ void
This method returns an undefined value.
Capture a tool invocation. Emits $mcp_tool_call (+ $exception on error).
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/posthog/mcp/client.rb', line 38 def capture_tool_call(tool_name, intent: nil, intent_source: nil, parameters: nil, response: nil, duration_ms: nil, is_error: false, error: nil, error_type: nil, category: nil, tool_description: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil, llm_model: nil, llm_model_source: nil) event = base_event(EventType::MCP_TOOLS_CALL, distinct_id, session_id, set_properties, groups, properties, , client_user_agent, vendor_client) event['resource_name'] = tool_name event['tool_description'] = tool_description event['tool_category'] = category event['protocol_version'] = protocol_version event['parameters'] = parameters event['response'] = response event['duration'] = duration_ms event['is_error'] = is_error == true event['error_type'] = error_type apply_intent(event, intent, intent_source) apply_model(event, llm_model, llm_model_source) if is_error event['error'] = Exceptions.capture_exception(error.nil? ? "Tool #{tool_name} returned an error" : error) end emit(event) end |
#capture_tools_list(tool_names: nil, parameters: nil, response: nil, duration_ms: nil, is_error: false, error: nil, error_type: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil) ⇒ void
This method returns an undefined value.
Capture a tools/list response. Emits $mcp_tools_list with $mcp_listed_tool_names.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/posthog/mcp/client.rb', line 84 def capture_tools_list(tool_names: nil, parameters: nil, response: nil, duration_ms: nil, is_error: false, error: nil, error_type: nil, protocol_version: nil, distinct_id: nil, session_id: nil, client_user_agent: nil, vendor_client: nil, set_properties: nil, groups: nil, properties: nil, timestamp: nil) event = base_event(EventType::MCP_TOOLS_LIST, distinct_id, session_id, set_properties, groups, properties, , client_user_agent, vendor_client) event['listed_tool_names'] = tool_names event['protocol_version'] = protocol_version event['parameters'] = parameters event['response'] = response event['duration'] = duration_ms event['is_error'] = is_error == true event['error_type'] = error_type event['error'] = Exceptions.capture_exception(error.nil? ? 'tools/list failed' : error) if is_error emit(event) end |
#prepare_tool_call(name, args = nil, input_schema: nil) ⇒ PreparedToolCall
Pull the agent's intent off the context argument and its self-reported
model off llm_model, strip the arguments this integration injected, and
flag the get_more_tools virtual tool. Hand intent/intent_source and
llm_model/llm_model_source straight to #capture_tool_call.
Pass the tool's own inputSchema (the same Hash you handed to
#prepare_tool_list) so a field the tool declares itself is left in
args and never read as analytics: only an injected argument is stripped
and reported. A composed (oneOf/allOf/anyOf) or $ref schema is never
injected into, so its fields are the tool's own and are left alone too.
Without the schema there is no way to tell the two apart, so the injected
names are always stripped.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/posthog/mcp/client.rb', line 173 def prepare_tool_call(name, args = nil, input_schema: nil) intent = tool_declares?(input_schema, 'context') ? nil : Intent.normalize(argument(args, 'context')) model = if @capture_model_option == false || tool_declares?(input_schema, ModelCapture::PARAM_NAME) nil else ModelCapture.normalize(argument(args, ModelCapture::PARAM_NAME)) end PreparedToolCall.new( args: strip_injected(args, input_schema), intent: intent, intent_source: intent ? 'context_parameter' : nil, llm_model: model, llm_model_source: model ? 'self_reported' : nil, is_missing_capability: name == @missing_capability_tool_name ) end |
#prepare_tool_list(tools, context: true, report_missing: false, capture_model: @capture_model_option) ⇒ Array<Hash>
Inject the context and llm_model arguments into
every tool descriptor (Hash with inputSchema) so agents state their intent,
and optionally append the get_more_tools virtual tool. Returns a new Array
of new Hashes. A tool whose schema is composed (oneOf/allOf/anyOf) or a
$ref is passed through untouched. Pass capture_model: false here or
to #initialize to disable model capture for this client, including
extraction from later tool calls.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/posthog/mcp/client.rb', line 129 def prepare_tool_list(tools, context: true, report_missing: false, capture_model: @capture_model_option) = Options.new(context: context, capture_model: capture_model) @capture_model_option = false unless .capture_model_enabled? = Options.new(context: context, capture_model: false) if @capture_model_option == false prepared = tools.map do |tool| next tool unless tool.is_a?(Hash) && (.context_enabled? || .capture_model_enabled?) name = SchemaMutation.fetch(tool, :name) || 'unknown' next tool if name == @missing_capability_tool_name schema = SchemaMutation.fetch(tool, :inputSchema) if .context_enabled? schema = SchemaMutation.add_context_parameter(schema, tool_name: name, description: .context_description) end if .capture_model_enabled? schema = SchemaMutation.add_model_parameter(schema, tool_name: name, description: .model_description) end tool.merge(SchemaMutation.key_for(tool, :inputSchema) => schema) end if report_missing && prepared.none? { |t| SchemaMutation.fetch(t, :name) == @missing_capability_tool_name } prepared << Tools.descriptor(@missing_capability_tool_name, ) end prepared end |