From 9fc042a5369fbb77f71a020a1358aaed293c630b Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Sun, 27 Sep 2026 11:19:08 +0200 Subject: [PATCH 1/2] fix: use shared instance tracking for MCP clients --- .changeset/tidy-mcp-lifecycle.md | 5 +++++ lib/posthog/client.rb | 4 ++-- spec/posthog/mcp/client_spec.rb | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 .changeset/tidy-mcp-lifecycle.md diff --git a/.changeset/tidy-mcp-lifecycle.md b/.changeset/tidy-mcp-lifecycle.md new file mode 100644 index 0000000..0a50a38 --- /dev/null +++ b/.changeset/tidy-mcp-lifecycle.md @@ -0,0 +1,5 @@ +--- +"posthog-ruby": patch +--- + +Fix instance tracking for client subclasses so `PostHog::MCP::Client` can initialize outside test mode and shut down without raising. diff --git a/lib/posthog/client.rb b/lib/posthog/client.rb index 73e21b7..f8e759e 100644 --- a/lib/posthog/client.rb +++ b/lib/posthog/client.rb @@ -187,7 +187,7 @@ def initialize(opts = {}) # Warn when multiple clients are created with the same API key (can cause dropped events) unless @disabled || opts[:test_mode] || opts[:disable_singleton_warning] - previous_count = self.class._increment_instance_count(@api_key) + previous_count = PostHog::Client._increment_instance_count(@api_key) if previous_count >= 1 logger.warn( 'Multiple PostHog client instances detected for the same API key. ' \ @@ -947,7 +947,7 @@ def shutdown(timeout: nil) flushed = false begin - self.class._decrement_instance_count(@api_key) unless @disabled + PostHog::Client._decrement_instance_count(@api_key) unless @disabled @feature_flags_poller&.shutdown_poller flushed = if @sync_mode diff --git a/spec/posthog/mcp/client_spec.rb b/spec/posthog/mcp/client_spec.rb index a371e3d..c658959 100644 --- a/spec/posthog/mcp/client_spec.rb +++ b/spec/posthog/mcp/client_spec.rb @@ -3,6 +3,20 @@ require_relative 'spec_helper' RSpec.describe PostHog::MCP::Client do + describe 'lifecycle' do + [{ test_mode: true }, { sync_mode: true }, {}].each do |options| + it "constructs and shuts down with #{options.inspect}" do + stub_request(:post, 'https://us.i.posthog.com/batch/').to_return(status: 200, body: '{}') + mcp_client = described_class.new({ api_key: 'phc_lifecycle' }.merge(options)) + mcp_client.capture_tool_call('echo', distinct_id: 'user') + + expect(mcp_client.shutdown(timeout: 2)).to be(true) + expect(mcp_client.shutdown(timeout: 2)).to be(true) + expect(mcp_client.capture(event: 'after shutdown', distinct_id: 'user')).to be(false) + end + end + end + let(:client) { described_class.new(api_key: 'phc_test', test_mode: true) } it 'captures tool calls with $lib override, anonymous distinct id and error scalars' do From b728ad1d2118b6492cfc3caeb6f7ffd8efae5b37 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Sun, 27 Sep 2026 15:19:48 +0200 Subject: [PATCH 2/2] fix: preserve registrations when untracked clients shut down --- .changeset/tidy-mcp-lifecycle.md | 2 +- lib/posthog/client.rb | 3 ++- spec/posthog/mcp/client_spec.rb | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.changeset/tidy-mcp-lifecycle.md b/.changeset/tidy-mcp-lifecycle.md index 0a50a38..0714c86 100644 --- a/.changeset/tidy-mcp-lifecycle.md +++ b/.changeset/tidy-mcp-lifecycle.md @@ -2,4 +2,4 @@ "posthog-ruby": patch --- -Fix instance tracking for client subclasses so `PostHog::MCP::Client` can initialize outside test mode and shut down without raising. +Fix instance tracking for client subclasses so `PostHog::MCP::Client` can initialize outside test mode and shut down without raising. Clients that skip registration no longer remove another client's duplicate-instance warning on shutdown. diff --git a/lib/posthog/client.rb b/lib/posthog/client.rb index f8e759e..17eae40 100644 --- a/lib/posthog/client.rb +++ b/lib/posthog/client.rb @@ -188,6 +188,7 @@ def initialize(opts = {}) # Warn when multiple clients are created with the same API key (can cause dropped events) unless @disabled || opts[:test_mode] || opts[:disable_singleton_warning] previous_count = PostHog::Client._increment_instance_count(@api_key) + @instance_registered = true if previous_count >= 1 logger.warn( 'Multiple PostHog client instances detected for the same API key. ' \ @@ -947,7 +948,7 @@ def shutdown(timeout: nil) flushed = false begin - PostHog::Client._decrement_instance_count(@api_key) unless @disabled + PostHog::Client._decrement_instance_count(@api_key) if @instance_registered @feature_flags_poller&.shutdown_poller flushed = if @sync_mode diff --git a/spec/posthog/mcp/client_spec.rb b/spec/posthog/mcp/client_spec.rb index c658959..8f68f78 100644 --- a/spec/posthog/mcp/client_spec.rb +++ b/spec/posthog/mcp/client_spec.rb @@ -4,6 +4,31 @@ RSpec.describe PostHog::MCP::Client do describe 'lifecycle' do + [PostHog::Client, described_class].each do |client_class| + [{ test_mode: true }, { disable_singleton_warning: true }].each do |options| + it "preserves live-client warnings after #{client_class} shuts down with #{options.inspect}" do + clients = [] + logger = PostHog::Logging.logger + allow(logger).to receive(:warn) + live = PostHog::Client.new(api_key: 'phc_shared_lifecycle') + clients << live + unregistered = client_class.new({ api_key: 'phc_shared_lifecycle' }.merge(options)) + clients << unregistered + + expect(unregistered.shutdown(timeout: 2)).to be(true) + expect(unregistered.shutdown(timeout: 2)).to be(true) + clients << PostHog::Client.new(api_key: 'phc_shared_lifecycle') + expect(logger).to have_received(:warn).with(a_string_including('Multiple PostHog client instances')).once + + clients.each { |instance| instance.shutdown(timeout: 2) } + clients << PostHog::Client.new(api_key: 'phc_shared_lifecycle') + expect(logger).to have_received(:warn).with(a_string_including('Multiple PostHog client instances')).once + ensure + clients.each { |instance| instance.shutdown(timeout: 2) } + end + end + end + [{ test_mode: true }, { sync_mode: true }, {}].each do |options| it "constructs and shuts down with #{options.inspect}" do stub_request(:post, 'https://us.i.posthog.com/batch/').to_return(status: 200, body: '{}')