Skip to content

feat(tracking): capture utm_source alongside client family#1106

Merged
dcramer merged 1 commit into
mainfrom
junior/feat-track-utm-source
Jun 17, 2026
Merged

feat(tracking): capture utm_source alongside client family#1106
dcramer merged 1 commit into
mainfrom
junior/feat-track-utm-source

Conversation

@dcramer

@dcramer dcramer commented Jun 17, 2026

Copy link
Copy Markdown
Member

What

Adds server-side UTM attribution tracking so traffic tagged with ?utm_source=plugin (added by getsentry/sentry-for-ai#191 to the AI plugin's MCP URL) is visible in spans and metrics.

Why

Without this, utm_source=plugin arrives at /mcp but never lands on any span attribute or metric dimension.

Changes

New: server/lib/attribution.ts

  • resolveUtmSource(raw) — buckets raw values into "plugin" | "other" | null (absent param → null, so absence and unknown stay distinct)
  • resolveUtmSourceFromUrl(url) — convenience wrapper that reads directly from a URL object
  • UTM_SOURCE_ATTRIBUTE = "app.utm_source" — exported constant used at every call site to prevent drift
  • Lives separately from client-family.ts (which is purely User-Agent → client family) and from the browser-side client/utils/attribution.ts (different known values, different API)

server/index.ts

  • Sets app.utm_source early on the active span for /mcp requests, matching the existing app.client.family early-annotation pattern. Covers requests that are rate-limited or rejected before reaching mcp-handler.ts.

server/lib/mcp-handler.ts

  • Sets app.utm_source on the active span for authenticated MCP requests (alongside the existing app.client.family, app.server.mode.agent, app.server.mode.experimental).

server/metrics.ts

  • getMcpRequestAttributes reads utm_source via resolveUtmSourceFromUrl
  • getMetricAttributes emits app.utm_source on the app.server.response metric for MCP routes
  • annotateTrackedRequestSpan sets app.utm_source on the span for MCP routes

server/lib/attribution.test.ts

10 tests: resolveUtmSource, resolveUtmSourceFromUrl, and constant value.

OTel note

OTel has no specific semantic convention for UTM params. Following the existing app.* custom namespace used by the rest of the codebase (app.client.family, app.server.mode.agent, etc.).

Verified

  • attribution.test.ts: 10/10 tests pass
  • Full mcp-cloudflare suite: 356/356 tests pass

View Session in Sentry

Add server-side resolveUtmSource() to client-family.ts that buckets the
raw utm_source query param into a fixed set of known values (currently
"plugin"; anything else maps to "other") to keep metric cardinality
bounded.

Wire it through:
- metrics.ts: getMcpRequestAttributes reads utm_source and both
  getMetricAttributes / annotateTrackedRequestSpan emit app.utm_source
  when present
- mcp-handler.ts: reads utm_source and sets app.utm_source on the
  active span

This lets traffic from the AI plugin (utm_source=plugin, added in
getsentry/sentry-for-ai#191) be attributed in traces and the
app.server.response metric.

Co-Authored-By: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
@dcramer dcramer force-pushed the junior/feat-track-utm-source branch from 3ce9264 to 2fa190f Compare June 17, 2026 23:12
@dcramer dcramer changed the title feat(tracking): capture utm_source param alongside client family feat(tracking): capture utm_source alongside client family Jun 17, 2026
@dcramer dcramer marked this pull request as ready for review June 17, 2026 23:15
@dcramer dcramer merged commit 5dee0fc into main Jun 17, 2026
18 checks passed
@dcramer dcramer deleted the junior/feat-track-utm-source branch June 17, 2026 23:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant