Skip to content

mcp(telemetry): $mcp_tool_call carries LoopOver's property names, so PostHog's MCP dashboards read empty #10175

Description

@JSONbored

Part of epic #8286 (Phase 2 — Analytics). Prerequisite for #8606.

Problem

The remote MCP server emits an event named $mcp_tool_call, but populates it with LoopOver's own snake_case property names (tool, ok, duration_ms, arguments, result) from buildMcpToolCallProperties in @loopover/contract.

PostHog's built-in MCP Analytics dashboards read the reserved $mcp_* names literally$mcp_tool_name, $mcp_duration_ms, $mcp_is_error, $mcp_parameters, $mcp_response. The event therefore ingests cleanly and every breakdown on it renders empty. Nothing surfaces the mismatch.

Two canonical events are also missing entirely:

  • $mcp_initialize — the handshake, and the only event that says which client connected (Claude Code vs Cursor vs a raw SDK script) and at what version.
  • $mcp_tools_list — discovery. Joined against $mcp_tool_call on $session_id, it is the difference between a tool nobody wants and a tool nobody could find.

There is no $session_id on any of them, so no cross-event funnel is possible.

Why now

The loopover PostHog project currently holds zero $mcp_tool_call events (POSTHOG_API_KEY was never set on the loopover-api Worker), so the wire shape can be corrected with no historical data to migrate or reconcile. That stops being true the moment the key is set.

Deliverables

  • $mcp_tool_call carries PostHog's canonical $mcp_* properties. LoopOver's own surface / transport / category dimensions ride alongside them (PostHog's custom-server docs sanction extra properties explicitly) — they have no canonical equivalent and a breakdown by them would otherwise be impossible.
  • usage_event is unchanged. It stays LoopOver's own minimal, payload-free event in its own vocabulary.
  • Add $mcp_initialize, sourced from the initialize request's own clientInfo — not the x-loopover-mcp-* headers, which only our published client sets and which would collapse every third-party client into one bucket.
  • Add $mcp_tools_list, reporting what this server registered rather than the cross-server contract registry.
  • $session_id (from Mcp-Session-Id), $mcp_source, and $mcp_server_* on every canonical event.
  • Project the closed MCP_TELEMETRY_ERROR_CODES set onto PostHog's closed $mcp_error_type set, so their error breakdown populates without losing the precise LoopOver code on usage_event.
  • The allowlist meta-test covers both vocabularies separately, so a native key can never quietly satisfy the canonical check or the reverse.

Boundaries

Dashboards and insights are #8606. Identity stays anonymous — this is fleet telemetry with no per-actor distinct_id (#6228), and that does not change here.

Resources

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions