Skip to main content
Glama
Clamp Analytics MCP Server

Clamp Analytics MCP Server

Official
by clamp-sh

@clamp-sh/mcp

Analytics MCP server for Clamp Analytics. Gives Claude, Cursor, VS Code Copilot, Windsurf, Cline, and any other Model Context Protocol client live access to your traffic, pages, events, revenue, errors, funnels, cohorts, and alerts — and the tools to create funnels and metric alerts from the conversation.

Using Cursor, VS Code, Claude Code, or Claude Desktop? Install the hosted Remote MCP from your Clamp dashboard in one click — auth happens automatically, no API key to paste, no config files to edit. Open a project, go to the Installation tab, and pick your editor. This npm package is the stdio path for CI, headless agents, custom clients, and self-hosted Clamp instances.

What your agent can do

Once connected, your agent can answer questions like:

  • "How's traffic this week?"

  • "Why did pageviews drop on Tuesday?"

  • "Which referrers drove the most signups?"

  • "Compare US vs Germany conversion this month"

  • "Create a funnel from /pricing to signup to purchase"

  • "Alert me when bounce rate on /pricing goes above 70%"

  • "What did Hacker News visitors actually read?"

  • "Run the weekly report"

Each question maps to one or two MCP tool calls. The agent picks the right tool, runs the query, and returns the answer in chat — no dashboard tab, no copy-paste.

Related MCP server: Google Analytics MCP Server

Why an MCP server for analytics

A dashboard answers the questions you anticipated when you built it. An MCP server lets the agent answer the ones you didn't. Same data, different access pattern: the agent can compose tools, follow up on its own answers, and run the diagnostic queries you'd manually click through.

Tools are typed (outputSchema per MCP protocol 2025-06-18), so client-side validation works before any call lands. Per-tool descriptions name the use case and a representative example, so agents pick the right tool on the first try.

Setup

For one-click install in Cursor / VS Code / Claude Code / Claude Desktop, use the Clamp dashboard. For stdio (CI, headless agents, custom clients, self-hosted instances), pick your client below.

VS Code / Copilot

Add to .vscode/mcp.json in your project:

{
  "servers": {
    "clamp": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@clamp-sh/mcp"],
      "env": {
        "CLAMP_API_KEY": "sk_proj..."
      }
    }
  }
}

Claude Code

claude mcp add clamp -- npx -y @clamp-sh/mcp
export CLAMP_API_KEY="sk_proj..."

Claude Desktop and Cursor

Both clients use the same shape — drop this into claude_desktop_config.json or .cursor/mcp.json:

{
  "mcpServers": {
    "clamp": {
      "command": "npx",
      "args": ["-y", "@clamp-sh/mcp"],
      "env": {
        "CLAMP_API_KEY": "sk_proj..."
      }
    }
  }
}

API key

Get your API key from the Clamp dashboard under Settings → API Keys. Keys are scoped to a project and start with sk_proj.

Tools

Free (all plans)

Tool

What it does

traffic.overview

Pageviews, visitors, sessions, bounce rate, avg duration. Period comparison included.

traffic.timeseries

Any core metric over time with automatic granularity (visitors, sessions, bounce rate, avg duration, or event counts).

traffic.compare

Compare any metric across two arbitrary periods. Returns absolute and percent delta.

traffic.live

Visitors active in the last N minutes plus their top pages, referrers, and countries.

breakdown

Group visitors and pageviews by any dimension: pathname, referrer_host, country, city, region, device_type, browser, browser_version, os, os_version, entry_page, exit_page, channel, or any UTM field.

pages.engagement

Per-page metrics with view: summary, engagement (adds engagement seconds and bounce rate), sections (per-section view counts for one pathname; needs the section-views SDK extension).

events.list

Custom event counts with property filtering and grouping.

events.observed_schema

The actual fired-event signature with per-property type observations. Diff against a local event-schema.yaml to surface schema drift.

events.property_values

Top distinct string-typed values a property has taken on a specific event. Use to discover the value space before defining a cohort filter or running events.list with property=.

revenue.sum

Sum Money-typed event properties. Split by currency, group by any dimension, switch to first-touch via attribution_model. Drill-in filters: plan, product, first_touch_dim + first_touch_value.

revenue.summary

The whole Revenue tab in one call: revenue per currency, customers, orders, AOV, LTV, MRR (running balance) + ARR, per-plan rollup (where mrr_contribution is the cumulative MRR balance per plan, not the in-period delta), per-product rollup, prior-period comparison, and mode flags (is_subscription, has_one_time) so callers can render conditionally.

revenue.retention

Cohort revenue retention on MRR-on-MRR math. Per window: mature_size, retained, retention_rate, mrr_at_d, baseline_mrr_mature, nrr (net revenue retention; >1.0 = net expansion). Anchored at the customer's first subscription_started.

revenue.timeseries

Plot one revenue metric per bucket. metric accepts revenue (Money sum), mrr (running balance, picks up from prior period), customers (distinct paying), or transactions (event count).

sessions.paths

Aggregate session paths: top entry → exit pairs with pages per session and duration.

users.journey

Chronological session-and-event reconstruction for one anonymous ID.

cohorts.create / cohorts.list / cohorts.retention / cohorts.compare / cohorts.delete

Define cohorts by event + period + filter; query retention curves; compare 2–10 cohorts side-by-side on the same retention windows; delete a saved cohort by name (definitions are immutable, so delete + re-create is the rename/restructure flow).

errors.list / errors.groups / errors.timeline / errors.context

Recent errors, fingerprint-grouped errors with affected-user counts, error rate over time, and breadcrumbs leading to a single error.

projects.list

List all projects this credential can access.

docs.search

Keyword-search the Clamp docs index.

Pro

Tool

What it does

funnels.create

Define and immediately evaluate a multi-step conversion funnel. Steps accept property predicates: cta_click[location=hero][plan=pro]. Names must be unique within a project — a duplicate-name call returns 409.

funnels.list

List funnels or fetch one with cohort filters (country, channel, device, UTM).

funnels.delete

Delete a saved funnel by name (definitions are immutable, so delete + re-create is the rename/restructure flow).

alerts.create

Set up metric alerts (e.g. "visitors drops_by 30% over 7d").

alerts.list / alerts.delete

List and remove alerts.

Common parameters

period — "today", "yesterday", "7d", "30d", "90d", or a custom range as "YYYY-MM-DD:YYYY-MM-DD". Defaults to "30d".

limit — Max rows returned, 1-50. Defaults to 10.

Filters — Most tools accept pathname, referrer_host, channel, country, region, city, device_type, browser, browser_version, os, os_version, and the full UTM set (utm_source, utm_medium, utm_campaign, utm_content, utm_term). Funnels accept the same cohort filters.

dimension (breakdown only) — see the breakdown row above for valid values.

view (pages.engagement only) — "summary" (default), "engagement", or "sections". "sections" requires pathname.

Revenue filters (revenue.sum, revenue.timeseries) — plan and product filter to one subscription tier or one-time product. first_touch_dim (one of channel, referrer_host, utm_campaign, utm_source, utm_medium, country) + first_touch_value filter to customers whose earliest session matched that dim. attribution_model="first_touch" on revenue.sum groups by first-touch dim instead of conversion-time dim.

Revenue metric (revenue.timeseries only) — "revenue" (default), "mrr" (running balance), "customers", or "transactions".

Prompts

Pre-built analytics workflows the AI can follow. Each prompt tells the model which tools to call, in what order, and how to present the results.

Prompt

What it produces

weekly_report

Traffic summary, top pages, referrers, countries, one actionable insight.

traffic_diagnosis

Root-cause analysis for traffic changes. Drills into channels, countries, devices, pages.

conversion_audit

Funnel drop-off analysis with cohort segmentation.

channel_breakdown

Traffic quality comparison across sources (volume vs engagement).

page_performance

Deep-dive on a single page: trends, referrers, devices, engagement vs site average.

Examples

Traffic overview

"How's my traffic this week?"

Calls traffic.overview with period="7d". Returns pageviews, visitors, sessions, bounce rate, avg duration, and comparison to the previous week.

"Show me visitor trends for the last 90 days"

Calls traffic.timeseries with period="90d" and metric="visitors". Returns daily counts over the full window.

Sources and geography

"Where are my visitors coming from?"

Calls breakdown with dimension="referrer_host". Returns referrer hostnames with channel classification (organic_search, direct, referral, paid, email, organic_social) joined into each row.

"Which cities in Germany have the most visitors?"

Calls breakdown with dimension="city" and country="DE". Returns city names with country and visitor/pageview counts.

"Is Safari 17 bouncing harder than Chrome 120?"

Two calls to traffic.overview with browser="Safari", browser_version="17" and browser="Chrome", browser_version="120". Returns side-by-side bounce rates.

"Which pages did Hacker News visitors read?"

Calls pages.engagement with referrer_host="news.ycombinator.com". Returns only pages viewed by traffic from HN.

Custom events

"How many signups happened this month?"

Calls events.list with name="signup" and period="30d".

"Break down signups by plan"

Calls events.list with name="signup" and group_by="plan". Returns counts per property value.

Funnels (Pro)

"Create a funnel from pricing page to signup to purchase"

Calls funnels.create with steps ["pageview:/pricing", "signup", "purchase"]. Returns step-by-step conversion rates immediately.

"Compare funnel conversion for US vs Germany"

Two calls to funnels.list, one with country="US" and one with country="DE".

Alerts (Pro)

"Alert me if visitors drop by 30% week over week"

Calls alerts.create with metric="visitors", condition="drops_by", threshold=30, period="7d".

Workflows

"Why did traffic drop last week?"

Follows the traffic_diagnosis prompt. Systematically checks channels, countries, devices, and pages to isolate the root cause.

"Audit our conversion funnel"

Follows the conversion_audit prompt. Analyzes funnel drop-offs, segments by device/country/channel, and identifies the biggest optimization opportunity.

Environment variables

Variable

Required

Description

CLAMP_API_KEY

Yes

Project API key (sk_proj...).

CLAMP_API_URL

No

Override the API base. Defaults to https://api.clamp.sh. Set this when pointing at a self-hosted Clamp instance.

FAQ

Do I need an API key for the Remote MCP install? No. Installing from the Clamp dashboard handles auth automatically. The API key is only needed for stdio (this npm package), self-hosted instances, CI, and custom clients.

How is this different from the Clamp dashboard? Same data, different access pattern. Use the dashboard when you want a chart; use the MCP when you want the agent to do the digging — diagnose a drop, compose a funnel, run a weekly report — and return the answer in chat.

Can I self-host? Yes. Point CLAMP_API_URL at your self-hosted Clamp instance, or run the stdio server inline in your own infrastructure. The MCP tools work against any Clamp API endpoint that speaks the same protocol.

Does this work for read-only audits, or can the agent make changes? Both. The Free-tier tools are all read-only except for cohorts.create / cohorts.delete; the Pro tools (funnels.create, funnels.delete, alerts.create, alerts.delete) also write. Scope the connection from the dashboard if you only want read access.

Will the agent know about my custom events? Yes. events.observed_schema returns the actual fired-event signatures with per-property type observations, so the agent can answer "what's in my event schema" without you pasting anything.

License

MIT

Available Tools

33 tools
alerts.createA
Idempotent

Create a metric alert that fires when a condition crosses its threshold. The alert monitors one metric (pageviews, visitors, sessions, bounce_rate, or avg_duration) over a rolling period and is re-evaluated every time an MCP session connects — alerts surface in-thread when the agent next checks, not as background pushes. Use to set lightweight monitoring like "alert me if pricing pageviews drop 50% week-over-week" or "alert me if bounce rate exceeds 70%". Requires Pro plan.

Examples:

  • pricing pageview drop → metric="pageviews", condition="drops_by", threshold=50, pathname="/pricing"

  • bounce rate ceiling → metric="bounce_rate", condition="above", threshold=70, period="7d"

  • traffic surge → metric="visitors", condition="increases_by", threshold=100, period="1d"

  • minimum-floor → metric="sessions", condition="below", threshold=100, period="1d"

Limitations: evaluation is on MCP session connect, not background — there are no push notifications, emails, or webhooks. Threshold for above/below is the absolute metric value; for drops_by/increases_by it is the percentage change vs the previous period of the same length. One alert per call — create multiple alerts for multiple conditions.

Inputs: metric (required), condition (required), threshold (required), period, +2 more. Returns: id, metric, period, pathname, +3 more

alerts.deleteA
DestructiveIdempotent

Delete an alert by its id. Find the id by calling alerts.list first. The deletion is irreversible — there is no soft-delete or undo, and the agent should confirm intent with the user before calling this on a non-trivial alert. Returns 404 if no alert with that id exists for the project.

Examples:

  • "remove the pricing alert" → alerts.list to find the matching id, then alerts.delete with that id

  • "clear all alerts" → alerts.list, then alerts.delete for each id

Limitations: irreversible. Does not return the deleted alert's prior configuration — capture it from alerts.list first if you may need to recreate it. The alert_id must be the UUID returned by alerts.list, not a metric name.

Inputs: alert_id (required), project_id. Returns: ok

alerts.listA
Read-only

List all alerts configured for a project. Returns each alert's id (UUID), metric, condition, threshold, period, optional pathname scope, and created_at timestamp. Use before alerts.delete to find the id you want to remove, or to show the user every monitor currently active. Returns an empty array if no alerts have been created yet.

Examples:

  • "what alerts do I have" → no extra params beyond project_id

  • before deleting → call alerts.list, locate the id, pass it to alerts.delete

Limitations: alert state (currently firing vs not) is not included — that surfaces only when the alert is re-evaluated on MCP session connect. There is no pagination; projects with many alerts return all of them in one response.

Inputs: project_id. Returns: alerts

cohorts.compareA
Read-only

Compare 2–10 saved cohorts side-by-side on retention. Returns each cohort's size and retention curve over the same period set, so you can read "did this week's signups retain better than last week's?", "is this experiment cohort behaving differently than control?", or stack a few quarters' cohorts to spot a structural retention trend — without composing the rates manually.

Order matters: the first name is treated as the primary; downstream renderings (dashboard, summaries) read deltas relative to it. Duplicates are silently deduplicated.

Examples:

  • "did April 14 signups retain better than April 7" → names="signups_apr_14,signups_apr_07"

  • "are pro-plan signups stickier than free" → names="pro_signups_q2,free_signups_q2"

  • "compare two onboarding variants out to 4 weeks" → names="onboarding_v1,onboarding_v2", periods="1w,2w,4w"

  • "three-arm experiment retention" → names="control,variant_a,variant_b"

  • "is signup retention improving quarter-over-quarter" → names="signups_q1,signups_q2,signups_q3,signups_q4"

Limitations: at most 10 cohorts per call. The same retention windows are applied to every cohort — there's no way to use different windows per slot. Sample-size caveats apply per cohort; check each size value before reading rate deltas (small cohorts make differences look meaningful when they aren't). Stacking many cohorts increases the multiple-comparisons risk — a divergent-looking row in a 5-way compare may just be random variation; commit to a hypothesis before reading.

Inputs: names (required), periods, project_id. Returns: cohorts

cohorts.createA

Create and persist a named cohort — a group of visitors defined by an event in a period, optionally narrowed by a property filter. The cohort is stored by name; subsequent retention/compare queries reference it without re-specifying the definition. Membership is recomputed at query time, not materialised, so the same cohort always reflects current data. Requires Pro plan.

The 0.x cohort definition is event-based: the user fired event between the period boundaries, optionally with filter.property == filter.value.

Examples:

  • "users who signed up the week of April 14" → name="signups_apr_14", definition={ event: "signup_completed", period: "2026-04-14:2026-04-20" }

  • "free-plan signups last month" → name="free_signups_last_month", definition={ event: "signup_completed", period: "30d", filter: { property: "plan", value: "free" } }

  • "customers who completed checkout in March" → name="checkouts_march", definition={ event: "checkout_completed", period: "2026-03-01:2026-03-31" }

Limitations:

  • Event-based definitions only in 0.x. Behavior cohorts ("did X then Y in order"), exclusion cohorts ("fired X but not Y"), and time-relative cohorts ("did X within N days of signup") aren't supported. The triggering event + period + property-filter shape covers ~95% of practical use cases.

  • Membership is computed at query time, so very large cohorts cost on every retention call.

  • Names must be lowercase alphanumeric with hyphens / underscores, and unique within a project — a duplicate-name create returns 409; use cohorts.delete first or pick a new name. Cohort definitions are immutable after creation; to change the event/period/filter, delete and re-create.

Pairs with: events.property_values to discover valid filter values before creating; cohorts.retention to read the curve once the cohort is created; cohorts.compare to stack 2–10 cohorts side-by-side; cohorts.list to discover existing cohort handles.

Inputs: name (required), definition (required), project_id. Returns: id, name, size, definition

cohorts.deleteA
DestructiveIdempotent

Delete a saved cohort by name. Irreversible — the agent should confirm intent with the user before calling this. The underlying event data isn't touched; only the cohort definition row is removed.

Examples:

  • "delete the test cohort" → name="test_cohort"

Limitations: irreversible. Returns 404 if the cohort doesn't exist. Definition is not returned before deletion — capture it from cohorts.list first if you may need to recreate it.

Inputs: name (required), project_id. Returns: ok

cohorts.listA
Read-only

List saved cohorts for the project. With name, returns one cohort with its current size; without, returns every cohort with their definitions. Use to discover cohort handles before calling retention or compare.

Examples:

  • "what cohorts do I have" → no params

  • "show me the signups_apr_14 cohort" → name="signups_apr_14"

Limitations: size is recomputed at fetch time and reflects current data — a cohort defined as "last 30d" returns a moving size as new events land.

Inputs: name, project_id. Returns: cohorts

cohorts.retentionA
Read-only

Compute the retention curve for a saved cohort. For each requested window (e.g. day 1, 7, 14, 30 after the cohort's anchor event), returns how many cohort members fired any non-pageview-end event in that day's window, and the rate vs the cohort size.

Use this to answer "did this cohort stick around?" and to compare retention across cohorts (via cohorts.compare). The 1-day-window resolution means "7d retention" is "day 7 specifically", not "anywhere in the first 7 days" — the standard product-analytics definition.

Examples:

  • "retention curve for signups_apr_14 at day 1, 7, 14, 30" → name="signups_apr_14" (default periods)

  • "weekly retention for customers_march out to 8 weeks" → name="customers_march", periods="1w,2w,4w,8w"

  • "did onboarding cohort A retain at week 1" → name="onboarding_v2", periods="7d"

Limitations: retention measures any non-pageview-end event presence. Custom retention metrics (e.g. "retained = fired purchase event") are not in 0.x. The numerator is computed per-day, so windows like "30d" return only day-30 activity. Cohort size is the denominator and is included in the response so consumers can apply sample-size discipline.

Pairs with: cohorts.compare to stack 2–10 cohorts side-by-side at the same windows; cohorts.list to discover available cohort names.

Inputs: name (required), periods, project_id. Returns: size, cohort, retention

docs.searchA
Read-only

Keyword-search the Clamp documentation index for setup, SDK, MCP, concepts, and skills pages. Returns ranked entries with url, title, and a short description. Each match scores higher when query terms appear in the title than the description; results are capped by limit (default 5, max 10). Use when the user asks how to do something — install Clamp in Next.js, add the section-views extension, set up Money revenue, write an alert — rather than what their data shows. For data questions use the analytics tools above.

Examples:

  • "how do I install Clamp in Vite" → query="vite install"

  • "how do I track revenue from Stripe" → query="stripe revenue server"

  • "what does the section-views extension do" → query="section views extension"

  • "how do I set up an alert" → query="alerts setup"

Limitations: keyword (substring) matching only — no semantic search, so synonyms ("webhook" vs "callback") will not match. The index is hand-maintained inside the MCP server and updates only when the server is redeployed. Returns an empty array if no matches; broaden the query rather than retrying with the same terms.

Inputs: query (required), limit. Returns: query, results

errors.contextA
Read-only

Breadcrumbs leading to one error: the events from the same session, before the error timestamp, in chronological order. The "what was the user doing right before this broke" view that turns a stack trace into a story.

Given an anonymous_id and the error's timestamp, finds the session that was active at that timestamp and returns up to limit events from that session strictly earlier than the error. Each event row has its name, url, pathname, referrer — enough to reconstruct the path the user took.

Examples:

  • "what did this user do right before the error" → anonymous_id from errors.list, before_timestamp = the error's timestamp

  • "show me the last 10 actions before this crash" → limit=10

  • "did the user click anything before the unhandled rejection" → check the events list for cta_click / button_clicked names

Limitations: scoped to the single session that contained the error. If the error fired during a session the user later ended (closed tab), and you pass a timestamp in a later session, you'll get that later session's events instead. Returns events only — not their full property bags (use users.journey if you need the per-event context). Default limit 20, max 100.

Pairs with: errors.list (source of the anonymous_id and timestamp pair); users.journey (multi-session view of the same user, when one-session breadcrumbs aren't enough).

Inputs: anonymous_id (required), before_timestamp (required), limit, project_id. Returns: count, events, anonymous_id, before_timestamp

errors.groupsA
Read-only

Errors deduplicated by server-computed fingerprint, with counts and first/last-seen timestamps. The triage view: which bugs are firing most often, affecting the most users, or showing up newly in the period.

Each group is one fingerprint (a bug). count is total occurrences in the period; users_affected is unique anonymous_ids that hit it; first_seen and last_seen show the time window the bug appeared in. sample_message and sample_type are pulled from one occurrence in the group — useful for the agent to surface a readable label.

Examples:

  • "what's broken right now" → period="today", sort_by="count"

  • "regressions that started this week" → period="7d", sort_by="first_seen"

  • "bugs hitting the most users" → sort_by="users_affected"

  • "what just stopped firing" → check last_seen against expected freshness

Limitations: only fingerprintable errors are grouped; events with neither message nor stack get a null fingerprint and are excluded from this view (they still show up in errors.list). Default sort is by count; pass sort_by to change. Default limit 50, max 200.

Pairs with: errors.list (drill into a fingerprint to see individual occurrences with full context); errors.timeline (chart the rate of one fingerprint to spot regressions or post-deploy regressions); traffic.compare (correlate a count spike with a deploy or campaign).

Inputs: limit, period, sort_by, project_id. Returns: groups, period, sort_by

errors.listA
Read-only

Recent error events with full context. One row per occurrence, returned newest-first. Each row carries the error itself (message, type, stack, fingerprint, handled flag) plus the standard event context (url, browser/OS/device, country, anonymous_id, session_id) — same shape ingest enriches every other event with, so an agent can correlate "errors here, traffic there" without joining a second tool.

Errors are written to the events table with name = "$error" by the SDKs' captureError() / window.onerror auto-capture. The server adds a stable error.fingerprint at ingest (sha256 of normalized message + first stack frame), so the same bug groups across occurrences regardless of which session or SDK reported it.

Examples:

  • "what errors fired today" → period="today" (no other filters)

  • "show me all TypeError occurrences this week" → message="", or use errors.groups first to find the fingerprint

  • "errors on Safari only" → browser="Safari"

  • "errors on the same fingerprint" → fingerprint=""

  • "only the auto-captured ones, not manual reports" → handled="false"

Limitations: returns up to limit rows (default 50, max 200). Stacks are stored verbatim from the SDK with no source-map resolution — production stacks will be minified for users on a build pipeline. For aggregate counts and dedup, use errors.groups; for breadcrumbs leading to one error, use errors.context.

Pairs with: errors.groups (find a noisy fingerprint, then list its occurrences here); errors.context (drill from one error row into the events from the same session that led to it); users.journey (full multi-session view of a user who hit an error).

Inputs: os, limit, period, browser, +6 more. Returns: count, errors, period

errors.timelineA
Read-only

Error count over time, bucketed hourly or daily. Optionally scoped to one fingerprint. Use this to spot regressions ("when did this bug start firing?") or post-deploy spikes ("did the deploy at 14:00 break something?").

Returns a series of { bucket, count } rows. The bucket field is an ISO timestamp aligned to the start of the interval. Buckets with no errors are omitted (sparse series); the agent should treat missing buckets as zero.

Examples:

  • "error rate over the last week" → period="7d" (returns hourly buckets)

  • "did errors spike after yesterday's deploy" → period="2d", interval="hour"

  • "is this specific fingerprint getting worse" → fingerprint="", period="30d"

  • "yearly error trend" → period="365d", interval="day"

Limitations: interval defaults to "hour" for periods ≤ 7 days, "day" otherwise. Override only if the default produces too few or too many buckets for the question. No fingerprint filter returns the total error rate across all bugs.

Pairs with: errors.groups (find which fingerprint is worth charting); traffic.timeseries (compare error count to traffic count to see if the rate is real or just a traffic surge); traffic.compare (week-over-week or before-after-deploy comparison).

Inputs: period, interval, project_id, fingerprint. Returns: period, series, interval, fingerprint

events.listA
Read-only

Lists custom events (user-defined). Excludes all $-prefixed system and canonical revenue events ($pageview, $pageview_end, $web_vital, $section_viewed, $error, $subscription_started, $subscription_canceled, …). Use revenue.* tools for billing events. Without a name filter, returns every custom event name in the period with its total count and unique-visitor count. To inspect a $-prefixed system event explicitly, pass it via name (e.g. name="$error"). With compare=true (only meaningful on the unfiltered list), each row also carries prev_count and prev_visitors from the equal-length previous period — events absent now but present before appear with count=0 (the "dead event" signal). With a name filter, returns the count for that single event, optionally filtered by a custom property key/value pair and grouped by another property key. Custom events are tracked client-side via clamp.track("event_name", { key: "value" }) or server-side via @clamp-sh/analytics/server.

Property values can be strings, numbers, or booleans (each stored in a separate column). When filtering or grouping by a numeric or boolean property, set value_type / group_by_type so the lookup hits the right column — otherwise the default ("string") will silently miss native number/boolean data. Use the project's event-schema.yaml to know each property's type.

Examples:

  • "what events are being tracked" → no params (lists all event names)

  • "how many signups this week" → name="signup", period="7d"

  • "signups grouped by plan" → name="signup", group_by="plan"

  • "paid signups only" → name="signup", property="plan", value="pro"

  • "checkouts where item count was 5" → name="checkout", property="count", value="5", value_type="number"

  • "events grouped by a numeric tier_id" → name="upgrade", group_by="tier_id", group_by_type="number"

Limitations: only one property/value pair per call. group_by only works when a name filter is set. Returns at most limit rows per group (default 10, max 50). For revenue use revenue.sum, which understands the Money type.

Inputs: os, city, name, limit, +22 more. Returns: events

events.observed_schemaA
Read-only

Return what's actually firing into ingest as a structured signature, for diffing against the project's authored event-schema.yaml. Scoped to custom user-defined events; all $-prefixed system and canonical revenue events are excluded by default. Different shape from the YAML — this is observation, not declaration.

Response shape:

{
  period: "30d",
  events: [
    { name: "checkout", count: 42,
      properties: {
        plan:  [{ type: "string", occurrences: 42 }],
        count: [{ type: "number", occurrences: 39 },
                { type: "string", occurrences: 3 }]   // ← drift!
      }}
  ]
}

Each property value is an array of typed observations. One entry = the key consistently fired with one type. Two-plus entries = same key fired under multiple storage columns in the period, which is exactly the silent-type-drift signal you want to surface (one call site sending count: 5, another sending count: "5").

Use it to compare declared vs reality:

  • Events declared, missing here → dead instrumentation

  • Events here, not declared → unauthored events firing

  • Properties on a declared event missing from the schema → silent property drift

  • properties[key].length > 1 → type-column collision; one of the call sites is sending the wrong type

Examples:

  • "what events are firing in production" → no params (defaults to 30d, excludes SDK-internal events)

  • "did the spec drift this week" → period="7d"

  • "include the SDK's automatic events too" → include_pageviews=true

Limitations: returns keys + types only, no property values. occurrences is row-level (each event firing counts), not unique visitors. Custom user-defined events only — all $-prefixed system and canonical revenue events ($pageview, $pageview_end, $web_vital, $section_viewed, $error, $subscription_started, $subscription_canceled, …) are excluded by default since the SDK owns their schema. Set include_pageviews=true to include them.

Pairs with: events.list for per-event volume context (this tool also returns count, but events.list supports filters and grouping); the local event-schema.yaml for declared-vs-observed diff.

Inputs: period, project_id, include_pageviews. Returns: events, period

events.property_valuesA
Read-only

Top distinct values a string-typed property has taken on a specific event in the period, ranked by occurrence count. Useful when you need to know the value space before filtering — e.g. cohort definitions, segment filters, "what values does plan take on signup?".

Response shape:

{
  event: "signup",
  property: "plan",
  period: "30d",
  values: [
    { value: "free", count: 412 },
    { value: "pro",  count: 138 },
    { value: "growth", count: 22 }
  ]
}

Examples:

  • "what plans do signups come from" → event="signup", property="plan"

  • "which countries hit the checkout event" → event="checkout", property="country" (only if you instrument it as a property; otherwise use traffic.breakdown)

  • "what source values does the lead event carry" → event="lead", property="source"

Limitations: STRING properties only — same scope as cohort filter values (cohort filtering is string-equality only). Numeric / boolean / money properties are out of scope. Results are capped at limit (default 50, max 200) so very high-cardinality properties (user_id, session_id, URLs) are truncated to the top-N by count. Events without the property at all are skipped — only rows where the property is present and non-empty contribute.

Pairs with: events.observed_schema to discover which properties an event actually fires; cohorts.create to scope a cohort by a specific value of those.

Inputs: event (required), property (required), limit, period, +1 more. Returns: event, period, values, property

funnels.createA

Create and immediately evaluate a conversion funnel. A funnel measures how many unique visitors complete an ordered sequence of steps. Returns step-by-step counts, per-step conversion rates (vs the previous step), and overall conversion (last step / first step). The funnel is saved by name and can be re-evaluated later or for different periods via funnels.list.

Step format: a custom event name like "signup", a pathname-scoped pageview like "pageview:/pricing", and optionally one or more property predicates appended in brackets — "cta_click[location=hero_primary]" matches only cta_click events whose location property equals "hero_primary". Stack predicates to AND them: "cta_click[location=hero_primary][plan=pro]". Predicates work on pageview steps too: "pageview:/pricing[utm_source=google]". Requires Pro plan.

Predicates default to string-typed comparisons (the property is read from the string column). For number- or boolean-typed properties (declared as such in event-schema.yaml), append a type tag: "purchase[count:n=5]" matches numeric 5; "checkout[refunded:b=false]" matches boolean false; "purchase[plan:s=5]" forces string matching when the value looks numeric. Tags: ":n" number, ":b" boolean (true|false|1|0), ":s" string (default).

Examples:

  • pricing-to-signup → name="pricing-to-signup", steps=["pageview:/pricing", "signup"]

  • which CTA actually converts → name="hero-cta-funnel", steps=["cta_click[location=hero_primary]", "signup_completed"]

  • onboarding flow → name="onboarding", steps=["signup", "onboarding_started", "onboarding_completed", "first_purchase"]

  • multi-item checkouts only → name="multi-item-checkout", steps=["pageview:/cart", "checkout_completed[items:n=3]"]

  • non-refunded purchases → name="purchase-net", steps=["pageview:/pricing", "purchase[refunded:b=false]"]

  • blog-to-newsletter → name="blog-newsletter", steps=["pageview:/blog", "newsletter_subscribed"]

Limitations: between 2 and 10 steps; step strings ≤500 chars; names ≤200 chars. Step order matters — once a session skips a step, it cannot complete later steps. Pageview pathnames match exact strings only (no wildcards). Predicate keys must be snake_case; string values may not contain ']' or '['. Number predicates require a finite value; boolean predicates require true|false|1|0. Funnel evaluation is per-session, not per-user across devices. Names must be unique within a project — a duplicate-name create returns 409; use funnels.delete first or pick a new name. Funnel definitions are immutable after creation; to change steps, delete and re-create.

Inputs: name (required), steps (required), project_id. Returns: id, name, steps, period, +1 more

funnels.deleteA
DestructiveIdempotent

Delete a saved funnel by name. Irreversible — the agent should confirm intent with the user before calling this. The underlying event data isn't touched; only the funnel definition row is removed.

Since funnel definitions are immutable post-creation, deleting and re-creating is the canonical way to "edit" a funnel — change the name or the steps, call delete, then funnels.create.

Examples:

  • "delete the old onboarding funnel" → name="onboarding-v1"

  • "remove the test funnel I just created" → name="test"

Limitations: irreversible. Returns 404 if the funnel doesn't exist. Definition is not returned before deletion — capture it from funnels.list first if you may need to recreate it.

Inputs: name (required), project_id. Returns: ok

funnels.listA
Read-only

Retrieve and re-evaluate a previously created funnel against current data for the specified period. Without a name, lists all funnels saved for the project. With a name, returns the same step-by-step counts and conversion rates as funnels.create, recomputed for the requested period and any cohort filters. Cohort filters (channel, country, device_type, utm_*) let you compare conversion across segments — e.g. mobile users from the US who came via organic search.

Examples:

  • list all funnels → no params

  • "how is pricing-to-signup converting this month" → name="pricing-to-signup", period="30d"

  • "mobile conversion for onboarding" → name="onboarding", device_type="mobile"

  • "paid traffic vs organic conversion" → call twice with channel="paid" then channel="organic_search"

Limitations: returns 404 if no funnel exists by that name — call funnels.list with no name first to enumerate. Cohort filters apply at the session level, not retroactively per step. Funnel definitions are immutable after creation (re-create with a new name to change steps).

Inputs: os, city, name, period, +15 more. Returns: funnels

pages.engagementA
Read-only

Per-page metrics with a selectable detail level. The view parameter chooses what comes back:

  • view="summary" (default): pathname, pageviews, visitors. Cheap; use as the standard "top pages" call.

  • view="engagement": adds avg_engagement_seconds (active tab time from the SDK's pageview_end beacon), bounce_rate (% of single-page sessions that started on this path), avg_max_scroll (mean max scroll % the user reached on the page), and scroll_distribution (cumulative % of pageview_end events that reached 25/50/75/100 scroll depth — answers "how far do visitors actually get down this page"). Use to answer "which pages hold attention", "which pages bounce", or "are visitors reading past the fold".

  • view="sections": returns per-section view counts for the specified pathname. Requires pathname to be set. Each section is a data-clamp-section element on that page, counted once per session when at least 40% scrolls into view. Use to answer "which parts of /pricing get seen" or "is the FAQ being read".

Examples:

  • "top pages this week" → view omitted (or "summary"), period="7d"

  • "which pages bounce hardest" → view="engagement", then sort by bounce_rate

  • "how far down /pricing do people scroll" → view="sections", pathname="/pricing"

Limitations: avg_engagement_seconds is null for pages without pageview_end data (SDK <0.3 or pages closed during navigation). view="sections" requires the section-views SDK extension installed (see /docs/sdk/extensions/section-views) and only counts elements with the data-clamp-section attribute — pages with no instrumented sections return []. view="sections" without pathname returns 400.

Inputs: os, city, view, limit, +16 more. Returns: pages, sections

projects.listA
Read-only

List all Clamp projects this credential can access. Returns each project's id, name, and plan ('free', 'pro', or 'growth'). Use this when the user asks "which sites do you see" or when the agent needs a project_id to disambiguate before calling another tool — most other tools auto-resolve when the credential has access to exactly one project, so explicit calls are only needed for multi-project setups.

Examples:

  • "which sites are tracked" → projects.list

  • before drilling into a specific project's data when several exist

Limitations: returns an empty array if the credential is org-scoped but has no projects yet. The plan field is the current billing plan, not a permission level.

Inputs: no named parameters. Returns: projects

revenue.retentionA
Read-only

Cohort revenue retention. Anchors each customer at their first cohort event (default: $subscription_started), then reports user retention + dollar retention at each anniversary window (default: 7d, 30d, 60d, 90d). Use this to answer "are customers sticking around?" and "is revenue churning, flat, or expanding?" in one round-trip.

Math is MRR-on-MRR: for each member, mrr_at_d is the cumulative sum of mrr_delta from anchor through anchor + d (the member's current MRR balance at that point). Retained members are mature members with mrr_at_d > 0. NRR = sum(mrr_at_d) / sum(baseline_mrr) across the same mature cohort — above 1.0 means net expansion (upgrades > churn), below 1.0 means contraction.

mature_size < size for windows the cohort isn't old enough to reach yet (e.g. a 90d window for a 30d-old cohort). Use mature_size to dim early windows or label them "cohort not yet mature".

Examples:

  • "what's our 30d retention" → read retention[].window=="30d".retention_rate (null when no mature members)

  • "are we expanding or contracting" → compare nrr across windows

  • "how many of last quarter's signups are still paying" → cohort_period="90d", read retention[].window=="90d"

Limitations: anchor is the user's FIRST cohort event in the period — re-subscribers after a cancel won't get a new anchor. The math assumes mrr_delta is set on $subscription_started / $subscription_canceled / plan-change events; events without mrr_delta don't affect the running balance. Canonical billing events are $-prefixed on the wire — pass cohort_event="$subscription_started" (not bare subscription_started) to anchor on them.

Inputs: windows, project_id, cohort_event, cohort_period, +2 more. Returns: size, cohort, currency, retention, +1 more

revenue.sumA
Read-only

Sum revenue from Money-typed event properties. Returns per-currency totals, optionally grouped by a traffic dimension (referrer_host, channel, country, device_type, pathname, utm_source/medium/campaign). Different currencies are never mixed in a single sum — each row is one (group, currency) pair. Money properties are tracked via clamp.track("purchase", { total: { amount: 29, currency: "USD" } }) — see /docs/concepts/revenue for the full Money type.

Attribution: attribution_model="last_touch" (default) groups revenue by the dimensions of the session where the revenue event fired — answers "what surface was active at conversion?". attribution_model="first_touch" joins each revenue event with the visitor's earliest-known session and groups by that session's acquisition dimension — answers "where did my paying customers actually come from?". For multi-visit funnels (typical B2B SaaS), first-touch is usually the more honest read on which channels actually drive revenue.

Examples:

  • "total revenue this month" → no group_by, period="30d"

  • "revenue by channel" → group_by="channel", period="30d"

  • "where did paying customers actually come from" → group_by="channel", attribution_model="first_touch"

  • "first-touch revenue per UTM campaign" → group_by="utm_campaign", attribution_model="first_touch"

  • "how much did Stripe purchases bring in from organic search" → event="purchase", channel="organic_search"

  • "top revenue countries" → group_by="country"

Limitations: events without any Money property contribute zero. If property is set, only that one Money key is summed; omitted, every Money property on matched events is included. Stripe-typed revenue (recommended) flows through server-side webhooks; client-only revenue is subject to ad-blocker loss. attribution_model="first_touch" only supports acquisition-dimension group_by (channel, referrer_host, utm_*); first-touch country / device / pathname are rejected because they're rarely what people actually mean by them. First-touch breakdowns are extremely sensitive to small samples — at single-digit paying-customer counts the per-channel rate is dominated by 1-2 users' acquisition history; check visitors before reading the rate, or pair with users.journey to validate against specific customers.

Inputs: os, city, event, limit, +19 more. Returns: rows

revenue.summaryA
Read-only

One call that returns the canonical revenue dashboard. Bundles every KPI tile + per-plan + per-product rollup + prior-period comparison into one round-trip. Use this for "how's revenue doing?" questions.

Returns:

  • total_revenue per currency (sum of every Money-typed property in the period)

  • revenue — total in the dominant currency (a flat number)

  • customers — distinct paying users (user_id when identified, else anonymous_id)

  • orders — count of revenue events in the period

  • aov — revenue / orders

  • ltv — mean cumulative revenue per paying customer to date

  • mrr — running balance of mrr_delta through period end (null when not subscription-shaped)

  • arr — mrr × 12 (null when mrr is null)

  • is_subscription — true when any plan carries non-zero mrr_contribution OR the project's running MRR is non-zero

  • has_one_time — true when any non-subscription revenue event landed in the period (mixed sellers have both flags set)

  • plans[] — per-plan rollup: revenue (in period), mrr_contribution (CUMULATIVE MRR balance per plan AT PERIOD END — forward-looking, not the in-period delta), customers

  • products[] — per-product rollup: groups by the reserved product property, falls back to event name when product isn't set

  • comparison — per-metric % change vs the prior period of equal length (revenue, customers, orders, aov, ltv, mrr, arr)

Examples:

  • "how's MRR this month" → period="30d", read mrr + comparison.mrr.change

  • "which plan has the most paying customers" → read plans[], sort by customers

  • "ARPU" → revenue / customers (no top-level field; divide client-side)

  • "top one-time products by revenue" → read products[], already sorted desc

  • "do we have any subscription customers" → read is_subscription

Limitations: MRR/ARR/per-plan mrr_contribution require events to carry the reserved mrr_delta numeric property (set automatically by clamp.revenue() with billing="monthly" or "annual"). MRR is reported only in the project's dominant currency; multi-currency MRR is a future scope. customers uses user_id when present (clamp.identify() was called) and falls back to anonymous_id otherwise — counts are honest but can over-count if a paying customer uses two devices without identifying. For per-channel/per-campaign revenue, use revenue.sum with group_by + attribution_model="first_touch". For cohort retention with NRR, use revenue.retention.

Inputs: os, city, period, region, +14 more. Returns: aov, arr, ltv, mrr, +11 more

search.landing_pagesA
Read-only

The pre-click → post-click join: Search Console metrics per landing page, joined to what those organic-search visitors actually did. For each page that gets search traffic, returns clicks / impressions / average position (from Search Console) alongside organic_sessions and conversions (from Clamp's own data). This answers the question Search Console alone can't: not "which pages get search clicks" but "which search traffic actually converts".

The join is page-level by necessity — Google strips the search query after the click, so conversions attribute to the landing PAGE, not to a specific query. conversion_event defaults to "signup_completed"; set it to whatever event marks a conversion for this project.

Use for "which search pages convert", "are our top-ranking pages actually working", "where is search traffic leaking".

Examples:

  • "which pages get search clicks but don't convert" → read pages, find high clicks + low conversions

  • "do our docs pages convert organic search" → scan pages for /docs paths

  • "conversions from search on /pricing" → conversion_event set, find the /pricing row

Limitations: page-level only — query→conversion cannot be attributed (see above). Search Console pages are matched to Clamp landing pages by URL path, so pages differing only by query string may not line up. organic_sessions counts sessions whose ENTRY channel is organic search. Requires a connected Search Console property.

Inputs: limit, period, project_id, conversion_event. Returns: pages

search.queriesA
Read-only

Google Search Console query metrics — the pre-click side of search. Returns the search terms that drove clicks to the site, with clicks, impressions, CTR (0–1), and average position (1 = top of results), over the period. Sourced from the project's connected Search Console property, synced daily; data lags ~2 days, same as Search Console itself.

Use for "what are we ranking for", "which queries drive the most clicks", "where do we sit on [term]".

Examples:

  • "top search queries this month" → period="28d", read queries (already sorted by clicks)

  • "what position are we for 'X'" → scan queries for X, read position

Limitations: only queries with at least one click in the period appear — Search Console omits zero-click impressions and anonymized rare queries, so totals won't match site-wide impressions. Requires a connected Search Console property (returns an empty list if none is connected). For what those clicks did AFTER landing — sessions and conversions — use search.landing_pages.

Inputs: limit, period, project_id. Returns: queries

sessions.pathsA
Read-only

Aggregate (entry_page → exit_page) session pairs. Returns the top pairs with how many sessions followed each path, average pages per session, average duration in seconds, and a bounce flag. Use to answer "what do visitors do after landing on /pricing", "where do sessions end", or "which entry pages lead to the deepest engagement". Aggregate-only — no per-user traces, no full pageview chains, no individual session reconstruction.

Examples:

  • "top entry → exit pairs last week" → period="7d"

  • "longest sessions starting from the blog" → pathname="/blog", min_pages=3

  • "where do paid-traffic visitors end up" → channel="paid"

Limitations: shows entry and exit only, not the full pageview chain in between (use events.list for granular event analysis). min_pages=1 (default) includes single-page sessions which always show as bounces; set min_pages=2 to exclude them. Sessions ending without a pageview_end beacon (e.g. browser crash) may have null durations.

Inputs: os, city, limit, period, +16 more. Returns: paths

traffic.breakdownA
Read-only

Aggregate visitors and pageviews grouped by a single dimension. The dimension parameter chooses what to group by — page paths, traffic sources, geography, devices, or marketing attribution. Results are sorted by visitors descending and capped by limit (default 10, max 50). Some dimensions return additional joined columns: dimension="referrer_host" includes the channel for each referrer; dimension="city" includes the ISO country code. All other dimensions return only {name, pageviews, visitors}. Filters narrow the set before aggregation.

Examples:

  • "top pages last week" → dimension="pathname", period="7d"

  • "who is sending traffic" → dimension="referrer_host"

  • "mobile vs desktop split" → dimension="device_type"

  • "best UTM campaigns" → dimension="utm_campaign"

  • "top cities in Germany" → dimension="city", country="DE"

  • "browser version distribution" → dimension="browser_version"

Limitations: aggregates pageview events only — for custom event breakdowns use events.list with group_by. The name column is the raw stored value (lowercase ISO codes for country, exact pathname strings including trailing slash). Per-page time-on-page or bounce rate is not included here — use pages.engagement for that.

Inputs: dimension (required), os, city, limit, +16 more. Returns: rows

traffic.compareA
Read-only

Compare one metric across two arbitrary periods side-by-side. Returns both period values plus absolute delta and percentage delta. Periods do not need to be the same length — the percentage delta normalizes by ratio so longer/shorter comparisons remain meaningful. Use when the user asks month-over-month, before-vs-after-launch, or "how does this week compare to last".

Examples:

  • "this month vs last month" → metric="visitors", a="2026-04-01:2026-04-25", b="2026-03-01:2026-03-31"

  • "is /pricing converting better since the redesign" → metric="pageviews", a="2026-04-16:2026-04-25", b="2026-04-01:2026-04-15", pathname="/pricing"

Limitations: one metric per call — for multi-metric comparison either call repeatedly or use traffic.overview (which always includes the previous period). Period strings must be a preset ("today", "yesterday", "7d", "30d", "90d") or a YYYY-MM-DD:YYYY-MM-DD range; relative phrases like "last quarter" are not parsed.

Inputs: a (required), b (required), metric (required), os, +16 more. Returns: a, b, metric, pct_delta, +1 more

traffic.liveA
Read-only

See who is on the site in the last N minutes. Returns the active visitor count plus top pages, top referrers, and top countries within that window. Defaults to 5 minutes; max 60. Use during incidents ("is anyone hitting the broken page right now"), launches ("is the new post getting traffic"), or whenever the user asks "who is on the site".

Examples:

  • "who is on the site right now" → window_minutes=5

  • "has anyone visited in the last hour" → window_minutes=60

  • "is the launch page getting hits" → window_minutes=15

Limitations: ingestion lag is ~30 seconds, so "live" is approximate. Visitor count is unique anonymous_ids in the window, not active sessions. For historical questions ("who visited last week"), use traffic.overview instead.

Inputs: project_id, window_minutes. Returns: visitors, pageviews, top_pages, top_sources, +2 more

traffic.overviewA
Read-only

High-level snapshot of website traffic over a period: total pageviews, unique visitors, sessions, bounce rate (%), and average session duration (seconds). Always includes a comparison block with the same metrics for the previous period of equal length plus the absolute and percentage delta. Use this as the first call when the user asks how the site is doing, before drilling into channels, pages, or funnels.

Examples:

  • "how is traffic this week" → period="7d"

  • "overview for last month" → period="30d"

  • "organic search performance this quarter" → period="90d", channel="organic_search"

Limitations: bounce_rate and avg_duration are derived from the SDK's pageview_end beacon — for SDK <0.3 they return null. Custom date ranges must be in YYYY-MM-DD:YYYY-MM-DD format. Maximum range is 365 days.

Inputs: os, city, period, region, +14 more. Returns: sessions, visitors, pageviews, comparison, +2 more

traffic.timeseriesA
Read-only

Traffic metrics over time as date buckets. Returns [{ date, count }] sorted ascending, where count is the chosen metric's value per bucket. By default charts pageview counts; set metric to chart unique visitors, sessions, bounce rate, or average session duration instead — the same KPIs as traffic.overview, broken out over time. Alternatively set event to count occurrences of a custom event. Granularity is automatic based on period length (hourly for ≤2 days, daily for ≤90 days, weekly for ≤365 days, monthly beyond) and can be overridden via granularity.

Examples:

  • "pageview trend last week" → period="7d"

  • "unique visitors per day this month" → metric="visitors", period="30d"

  • "is bounce rate climbing?" → metric="bounce_rate", period="90d"

  • "signups per day this month" → event="signup", period="30d", granularity="day"

  • "hourly pageviews yesterday" → period="1d", granularity="hour"

Limitations: metric and event are mutually exclusive — when metric is set it always scopes to pageviews and ignores event/property/value. bounce_rate is a percentage (e.g. 47.2), avg_duration is in seconds. Forcing granularity="hour" over a 90-day period produces hundreds of buckets and may be truncated server-side. Buckets with no matching events return zero (the series does not skip missing dates).

Inputs: os, city, event, value, +19 more. Returns: series

users.journeyA
Read-only

Chronological session history for a single visitor (anonymous_id). Each session collapses to one row showing the start and end timestamps, entry page, channel, referrer host, UTMs, country, device type, pageview count, and the distinct custom event names fired in that session. The first row is flagged with is_first_touch: true.

Use this when an aggregate query can't answer the question — multi-touch attribution analysis, support / debugging investigations ("what did this user do before signing up?"), or sanity-checking a specific account's journey before drawing conclusions about a cohort.

Examples:

  • "show me what this user did across all their visits" → anonymous_id="anon_xxx"

  • "where did this paying customer first come from?" → anonymous_id="anon_xxx", read sessions[0].channel

  • "did they convert on their first visit or come back later?" → check if signup-event sessions match is_first_touch=true

Limitations: requires the visitor's anonymous_id, which the SDK exposes via getAnonymousId() on the browser side. Aggregate questions ("which channels drive my best users") use revenue.sum with attribution_model="first_touch" — don't loop this tool over thousands of users. Defaults to a 365-day lookback so first-touch is meaningful; tighten with period if needed.

Pairs with: revenue.sum(attribution_model="first_touch") to validate the aggregate first-touch read against specific high-value customers' actual paths.

Inputs: anonymous_id (required), limit, period, project_id. Returns: sessions, anonymous_id

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 33 tools

    Disambiguation5/5

    Every tool targets a distinct resource and analytical view: events.list, events.observed_schema, and events.property_values cover counts, schema drift, and value space respectively, while traffic, revenue, errors, funnels, cohorts, and search tools each have clearly separated purposes. Cross-references and 'Pairs with' notes reinforce tool selection.

    Naming Consistency4/5

    Names consistently use lowercase dotted domain prefixes (events.*, revenue.*, errors.*) with snake_case, making categories predictable. The second segment mixes verbs (list, create, delete, compare) with noun views (overview, breakdown, observed_schema, journey), so it is not a uniform verb_noun pattern but remains coherent.

    Tool Count2/5

    At 33 tools the surface exceeds the 25+ threshold and is heavy for an agent to hold in context, even though the analytics domain is broad. The clear namespacing prevents chaos, but the sheer number of tools makes the server feel over-packed for an MCP endpoint.

    Completeness5/5

    The tool set covers the full analytics lifecycle: traffic and event measurement, revenue/summary/retention, funnels, cohorts, errors, alerts, search-console integration, user journeys, and session/page engagement. Saved objects support create/list/delete with documented delete-and-recreate workflows for edits, so there are no dead ends for the stated scope.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers