Clamp Analytics

MCP server for Clamp—Web analytics for the agentic era, built for indie hackers and small teams shipping with AI.

От сообщества: Добавлен пользователем или импортирован; проверьте владельца перед подключениемРаботаетБез входаГлобальныйБесплатноМожет изменять данные

Что умеет

  • Projects.List: 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
  • Traffic.Overview: High-level snapshot of website traffic over a period: total pageviews, unique visitors, sessions, bounce rate (%), and average session duration (seconds). Always includes a compariso
  • Events.List: Lists custom events (user-defined). Excludes all $-prefixed system and canonical revenue events (`$pageview`, `$pageview_end`, `$web_vital`, `$section_viewed`, `$error`, `$subscription_st

Какие данные видит

Нужен ли аккаунт

Не нужен: сервер работает без входа

MCP server for Clamp—Web analytics for the agentic era, built for indie hackers and small teams shipping with AI.

"Did the /pricing redesign I shipped yesterday hurt checkout?" "Build me a funnel from landing to first paid event and alert if CVR drops." "Is my newsletter driving paid signups, or just vanity?"

Tools for traffic, referrers, live visitors, events, revenue, session paths, and funnels. Query the data, build new funnels, create alerts.

Pair with our analytics skills for senior-analyst interpretation: MECE hypothesis trees, fingerprints for tracking regressions, and benchmarks calibrated to your business.

Список инструментов сервера (33)

Технические названия из tools/list. Нужны только разработчикам.

projects.listList 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.
traffic.overviewHigh-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.
events.listLists 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.
events.observed_schemaReturn 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.
events.property_valuesTop 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.
revenue.sumSum 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.
revenue.summaryOne 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.
revenue.retentionCohort 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.
users.journeyChronological 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.
errors.listRecent 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="<known message>", or use errors.groups first to find the fingerprint - "errors on Safari only" → browser="Safari" - "errors on the same fingerprint" → fingerprint="<from errors.groups>" - "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).
errors.groupsErrors 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).
errors.timelineError 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="<hex>", 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).
errors.contextBreadcrumbs 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).
traffic.timeseriesTraffic 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).
funnels.createCreate 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.
funnels.listRetrieve 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).
funnels.deleteDelete 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.
cohorts.createCreate 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.
cohorts.listList 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.
cohorts.retentionCompute 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.
cohorts.compareCompare 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.
cohorts.deleteDelete 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.
alerts.createCreate 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.
alerts.listList 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.
alerts.deleteDelete 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.
traffic.breakdownAggregate 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.
traffic.compareCompare 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.
sessions.pathsAggregate (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.
pages.engagementPer-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.
search.queriesGoogle 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`.
search.landing_pagesThe 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.
traffic.liveSee 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.
docs.searchKeyword-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.