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: false) ⇒ Array<Hash>
Inject the
contextargument (and, withcapture_model,llm_model) 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.
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/posthog/mcp/client.rb', line 20 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 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.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/posthog/mcp/client.rb', line 63 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.
102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/posthog/mcp/client.rb', line 102 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).
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/posthog/mcp/client.rb', line 35 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.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/posthog/mcp/client.rb', line 81 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.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/posthog/mcp/client.rb', line 166 def prepare_tool_call(name, args = nil, input_schema: nil) intent = tool_declares?(input_schema, 'context') ? nil : Intent.normalize(argument(args, 'context')) model = if 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: false) ⇒ Array<Hash>
Inject the context argument (and, with capture_model, llm_model) 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.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/posthog/mcp/client.rb', line 124 def prepare_tool_list(tools, context: true, report_missing: false, capture_model: false) = Options.new(context: context, capture_model: capture_model) 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 |