⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
packages/loopover-mcp/lib/telemetry.ts:33-62's recordMcpToolCall constructs a new PostHog
client per call, calls client.capture(...), and returns — synchronously, fire-and-forget, with no
flush()/shutdown() call and no client reuse.
The remote counterpart, src/mcp/telemetry.ts:60-86, does await client.flush() — added by commit
80f4aec99 ("fix(mcp): defer tool-call telemetry via waitUntil instead of firing it forgotten",
closing #7233) specifically because "capture() itself is fire-and-forget and returns before that
request lands," meaning without an awaited flush the event can be silently dropped if the runtime
tears down before the network POST completes.
recordStdioToolTelemetry (bin/loopover-mcp.ts:1715) is the single chokepoint every one of the
~37 registerStdioTool-registered MCP tools funnels through. If the local stdio server process
exits shortly after a tool call (the client disconnects, or the --stdio process is killed), the
in-flight PostHog network request for that call — and every earlier call whose client was never
shut down — can be silently dropped. Additionally, a new, un-closed PostHog client instance
accumulates in memory per call over a long-running session.
Requirements
- Await
client.flush() (or client.shutdown(), whichever the PostHog Node SDK recommends for a
short-lived process) before recordMcpToolCall returns, mirroring the remote
src/mcp/telemetry.ts fix exactly.
- Consider reusing a single client instance across calls within the same process lifetime rather
than constructing a new one per call, if that doesn't complicate the flush-on-exit guarantee (state
the chosen approach explicitly in the PR description either way).
Deliverables
All three Deliverables are required in the same PR.
Test Coverage Requirements
packages/loopover-mcp/** is measured by codecov/patch (99%+ target, branch-counted). The new
test must exercise the previously-synchronous, fire-and-forget path directly, proving the fix
actually awaits completion.
Expected Outcome
Local MCP CLI telemetry events are not silently dropped when the stdio process exits shortly after a
tool call, matching the guarantee already provided by the remote MCP telemetry path.
Links & Resources
Context
packages/loopover-mcp/lib/telemetry.ts:33-62'srecordMcpToolCallconstructs a newPostHogclient per call, calls
client.capture(...), and returns — synchronously, fire-and-forget, with noflush()/shutdown()call and no client reuse.The remote counterpart,
src/mcp/telemetry.ts:60-86, doesawait client.flush()— added by commit80f4aec99("fix(mcp): defer tool-call telemetry via waitUntil instead of firing it forgotten",closing #7233) specifically because "
capture()itself is fire-and-forget and returns before thatrequest lands," meaning without an awaited flush the event can be silently dropped if the runtime
tears down before the network POST completes.
recordStdioToolTelemetry(bin/loopover-mcp.ts:1715) is the single chokepoint every one of the~37
registerStdioTool-registered MCP tools funnels through. If the local stdio server processexits shortly after a tool call (the client disconnects, or the
--stdioprocess is killed), thein-flight PostHog network request for that call — and every earlier call whose client was never
shut down — can be silently dropped. Additionally, a new, un-closed
PostHogclient instanceaccumulates in memory per call over a long-running session.
Requirements
client.flush()(orclient.shutdown(), whichever the PostHog Node SDK recommends for ashort-lived process) before
recordMcpToolCallreturns, mirroring the remotesrc/mcp/telemetry.tsfix exactly.than constructing a new one per call, if that doesn't complicate the flush-on-exit guarantee (state
the chosen approach explicitly in the PR description either way).
Deliverables
recordMcpToolCallawaits a flush/shutdown before returning, so its caller can be confidentthe telemetry event has actually been sent (or definitively failed) before the process exits.
posthog-node, mirroring the pattern intest/unit/mcp-local-telemetry.test.ts) with a capture call that resolves on a delay, assertingrecordMcpToolCall's returned promise does not resolve until the mocked flush/network callcompletes — currently it resolves synchronously regardless.
path is unchanged).
All three Deliverables are required in the same PR.
Test Coverage Requirements
packages/loopover-mcp/**is measured bycodecov/patch(99%+ target, branch-counted). The newtest must exercise the previously-synchronous, fire-and-forget path directly, proving the fix
actually awaits completion.
Expected Outcome
Local MCP CLI telemetry events are not silently dropped when the stdio process exits shortly after a
tool call, matching the guarantee already provided by the remote MCP telemetry path.
Links & Resources
packages/loopover-mcp/lib/telemetry.ts:33-62(recordMcpToolCall, the function to fix)src/mcp/telemetry.ts:60-86(the already-correct remote counterpart, fixed by commit80f4aec99/MCP tool-call telemetry fires without waitUntil, unlike this repo's own scheduleAfterResponse pattern #7233)bin/loopover-mcp.ts:1715(recordStdioToolTelemetry, the chokepoint every stdio tool callfunnels through)
test/unit/mcp-local-telemetry.test.ts(existing test file to extend)