fix(mcp): await the telemetry flush so events are sent before the CLI returns - #8727
fix(mcp): await the telemetry flush so events are sent before the CLI returns#8727tryeverything24 wants to merge 1 commit into
Conversation
…pToolCall resolves recordMcpToolCall constructed a PostHog client, called capture(), and returned synchronously -- fire-and-forget, with no awaited flush. Since capture() returns before the network POST lands, a --stdio process that exits shortly after a tool call silently dropped the in-flight event. Mirror the remote src/mcp/telemetry.ts fix (80f4aec, JSONbored#7233) exactly: recordMcpToolCall is now async and awaits client.flush() after capture, inside the same never-throw catch. The guarantee is threaded through the stdio chokepoint -- recordStdioToolTelemetry awaits the wrapper and registerStdioTool awaits it on both the success and throw paths -- so the event is on the wire before the tool response goes out. Per-call client construction is kept (not cached): with flushAt 1, flushInterval 0 and the flush awaited to completion, each client holds no queued events or pending work by the time the promise resolves, so nothing accumulates across a long session. Closes JSONbored#8690
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
1 similar comment
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #8727 +/- ##
==========================================
- Coverage 90.56% 82.30% -8.26%
==========================================
Files 96 98 +2
Lines 22490 24762 +2272
Branches 3884 4746 +862
==========================================
+ Hits 20367 20381 +14
- Misses 1945 4203 +2258
Partials 178 178
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Caution 🛑 LoopOver review result - fixes requiredReview updated: 2026-07-26 01:15:27 UTC
Review summary Nits — 5 non-blocking
CI checks failing
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
|
LoopOver is closing this pull request on the maintainer's behalf (CI is failing (codecov/patch)). This is an automated maintenance action — to pursue this change, please open a new pull request with the issues resolved. Closed PRs may be analyzed later to improve review accuracy, but they are not automatically reopened or re-reviewed. |
Closes #8690
What
recordMcpToolCallinpackages/loopover-mcp/lib/telemetry.tsconstructed aPostHogclient, calledcapture(...), and returned synchronously — fire-and-forget, with no awaited flush.capture()returns before the network POST lands, so if the--stdioprocess exits shortly after a tool call (client disconnect, process kill), the in-flight event is silently dropped. The remote counterpart (src/mcp/telemetry.ts) was already fixed this way in80f4aec99/#7233; this PR gives the local wrapper the same guarantee.How
packages/loopover-mcp/lib/telemetry.ts—recordMcpToolCallis nowasyncand doesawait client.flush()aftercapture(...), mirroring the remotesrc/mcp/telemetry.tsfix exactly (sameflushAt: 1, flushInterval: 0config, same flush-before-resolve contract, same never-throw/never-reject catch). In the installed posthog-node v5,flush()is the public completion API (shutdownis underscore-private), soflush()is what the SDK offers a short-lived process — and it is what the remote wrapper uses.packages/loopover-mcp/bin/loopover-mcp.ts— the guarantee is threaded through the single chokepoint:recordStdioToolTelemetryawaitsrecordMcpToolCall, andregisterStdioToolawaits the telemetry on both its success and throw paths, so the event is on the wire before the stdio tool response goes out.Client-reuse decision (stated per the issue): I kept per-call client construction rather than caching a single client. Reusing one instance would complicate the flush-on-exit guarantee (a shared client's queue couples calls together, and cache invalidation on env change adds a second code path), while the memory concern is already resolved by the awaited flush: with
flushAt: 1, flushInterval: 0and the flush awaited to completion, each client holds no queued events, timers, or pending work by the timerecordMcpToolCallresolves, so it is immediately collectible — nothing accumulates over a long session. This also keeps the local wrapper byte-for-byte parallel to the remote one.Deliverables
recordMcpToolCallawaits a flush before returning — its caller knows the event has been sent (or definitively failed) before the process can exit.test/unit/mcp-local-telemetry.test.ts("does not resolve until the mocked flush/network call completes (fix(mcp): local MCP CLI telemetry never awaits/flushes its PostHog client before the process can exit #8690)"): the mocked flush resolves on a delay; the test proves the returned promise is still pending after the capture while the mocked network call is in flight, and resolves only after it completes. Also added: flush-called assertion on the happy path and a "never rejects when flush itself fails" case, mirroringtest/unit/mcp-telemetry.test.ts.Before / after
On unfixed
main(fix stashed), the updated test file fails 5/13 — including the new pending-promise test, becauserecordMcpToolCallreturnedundefinedsynchronously:With the fix:
The subprocess chokepoint suite (
test/unit/mcp-local-telemetry-chokepoint.test.ts, real PostHog SDK against a local recorder) and the in-process mcp-cli suites (which exercise bothrecordStdioToolTelemetrycall sites, success and throw) also pass unchanged.