MCP server
Ask your analytics from Claude, Codex, Cursor or any MCP client. ClickClacks runs a read-only, remote MCP server with twelve tools, scoped by a key, and every answer says where its numbers came from.
Updated
Want to see it first? The MCP feature page shows a conversation. This page covers setup and every detail. Sending events in, rather than reading them out? That’s the server API.
Endpoint
POST https://app.clickclacks.io/api/v1/mcp
Authorization: Bearer cka_live_…
Content-Type: application/json- Streamable HTTP, stateless and tools-only. Clients send JSON-RPC with
POST. There are no sessions and noMcp-Session-Id. GETand SSE streaming aren’t supported: aGETwith a valid key gets405.- Supports the
initialize,ping,tools/listandtools/callmethods, and protocol versions from2024-11-05to2025-11-25. - Every tool is read-only. Nothing a client does can change your data.
Keys
MCP uses MCP & analytics keys (cka_live_…). Create one in Settings › API access: give it a name such as “Reporting agent”, and copy the secret, which is shown once. Settings › MCP shows the server URL, whether you have an active key, and the setup snippets.
- The key decides the project. Tool arguments never take an organization or project ID; a call that includes one is refused.
- Read-only, with the single scope
analytics:read. - Revoke a key in Settings › API access to disconnect every client that uses it. Missing, invalid or revoked keys get HTTP
401. - Creating and revoking keys needs permission to manage API credentials in your organization.
Connect a client
Pick your client. Every configuration needs the same two things: the URL and the Authorization header.
claude mcp add --transport http clickclacks https://app.clickclacks.io/api/v1/mcp --header "Authorization: Bearer cka_..."The command Settings › MCP shows. Replace cka_... with your key.
To check it worked, ask the client to list its ClickClacks tools, or ask “Which ClickClacks sources are sending events?”, which calls list_sources.
Tools
Nine read-only tools for reports, and three more for companies. Every one also takes optional start and end dates. Schemas are strict: an argument that isn’t listed is refused.
| Tool | Arguments besides dates | Returns |
|---|---|---|
overview | Report filters. | Overview KPIs and the visitors series. |
list_funnels | Dates only. | Saved funnels: steps, ordering, count mode and window, with summary metrics when available. |
funnel_results | funnelId (required); breakdown, an event property. | One saved funnel run in its saved ordered or unordered, people or events mode, optionally split by the property. |
retention | Report filters; interval: daily, weekly (default) or monthly. | The saved retention definition and its cohort grid. |
list_events | Report filters; search, an exact event name; cursor, for the next page. | Event rows, a page at a time: pass the returned cursor for the next page. |
event_breakdowns | Report filters; dimension: pages, referrers, countries or events. | Top pages, referrers, countries and event names, or just one of them. |
list_segments | Dates only. | Saved segments, with people counts when available (otherwise null). |
list_sources | Dates only. | Sources with their event and people counts for the window, and health from each one’s last event. |
friction_findings | state: happening (default), stopped or muted. | Friction findings in that state last seen in the window. people7d is the finding’s rolling seven-day count. |
Tools for companies
A group is an account your events are stamped with: a company, a workspace, a team. Each project names its own types, so start with list_group_types. Companies and groups explains the model.
| Tool | Arguments besides dates | Returns |
|---|---|---|
list_group_types | Dates only. | The group types the project shows, in order: groupType (the key, such as company), labelSingular and labelPlural. Hidden types are left out. Call it first. |
list_groups | groupType (required); search, matched against the ID and the name; sort: last_seen (default), events, members or name; cursor, for the next page. | A page of 50 groups of that type: groupId, name, firstSeen, lastSeen, events and activeMembers in the window, and totalEvents. total counts every match, and page.nextCursor continues the list. |
group_profile | groupType and groupId (both required). | One group: its name, first and last seen, total events, traits, traitsWithheld and traitsUpdatedAt; metrics and previousMetrics (activeMembers, events, activeDays for the window and the equally long window before it); and members. |
event_breakdowns | Its usual arguments, plus count_by, a groupType. | The same four lists, counting unique groups of that type instead of people. countBy names the type. Each row has label, count and unstampedShare; event rows also have eventCount. |
funnel_results | Its usual arguments, plus count_by, a groupType. | The saved funnel counting the groups of that type instead of people. countBy names the type and counted is its plural, such as companies. Every count that reads people on a people funnel reads count, the count mode reads unique_companies, and results.unstampedShare is the share of the funnel’s step events that carry no group of the type. |
retention | Its usual arguments, plus count_by, a groupType. | The retention grid with cohorts of groups of that type instead of people. countBy and counted name the type, totalPeople reads totalCount, and results.unstampedShare is the share of the report’s events that carry no group of the type. |
What these tools return is about companies, never about a person:
- Traits. Secrets are dropped, as everywhere in ClickClacks. So is any trait that holds personal data: one named like
email,phone,ip,addressorpassword, or whose value reads as an email address or a phone number. Those traits are named intraitsWithheldand their values aren’t returned, even when the source is allowed to store them. Anamethat reads as an email address or a phone number comes back asnull. - Members are never identified.
members.activeis how many people had events for the group in the window.members.toplists up to ten of the most active asrank,eventsandshareOfEvents, with no person ID, name, email or session. No argument turns identifiers on. - A
groupTypethe project doesn’t show, because it is unknown or hidden in Lexicon, is refused with a pointer tolist_group_types.
With count_by on event_breakdowns:
- Only events stamped with a group of that type count.
unstampedShareis the share, from 0 to 1, of the row’s events that carry none and are left out. - Pages, referrers and countries are read from pageviews. A group sits in every row it has a pageview for, so rows can add up to more than the number of groups.
- Each list holds the five values with the most events, ordered by unique groups.
- Report filters apply as usual, and can name a group:
g:companyis the group’s ID, andg:company.plana trait of the event’s group, as in{ "property": "g:company.plan", "values": ["pro"] }.
With count_by on funnel_results and retention:
- A group enters a funnel at its first step-one event in the window, from any member, and completes each later step on the earliest matching event stamped with that group, again from any member, inside the conversion window. So when one person signs up and a colleague sends the first invite, the company converts.
- A retention cohort holds the groups whose first starting event falls in the period. A group is back in a later period when any of its members’ events is.
- A funnel counted by a group type is never counted by session: sessions belong to people, so a funnel saved in events mode is read as unique groups.
- Left out, the report counts what it was saved to count: a funnel or retention definition saved with Count by Companies answers in companies.
list_funnelsdoes the same for each funnel it lists, withcountedon the definition and on its row. - A saved Count by whose type the project no longer shows, because it is hidden in Lexicon, is read counting people.
- With an agency key, these tools take the
projectargument like every other tool. - If the project’s group data can’t be read, the answer is
degradedwith a reason anddata: null.
Questions to ask
You ask in plain language; the client picks the tools. Some that work well, and the tools they use:
| Ask | Tools |
|---|---|
| “Which step of our signup funnel loses the most people this month? Show the numbers.” | list_funnels, funnel_results |
| “Compare weekly retention for the last eight weeks. Is any cohort clearly weaker?” | retention |
| “What are our top referrers this month, and how does that compare with last month?” | event_breakdowns |
| “Is every source still sending? Which one went quiet?” | list_sources |
| “What friction is happening right now, and on which pages?” | friction_findings, event_breakdowns |
| “How many visitors did we have yesterday, only on the pro plan?” | overview with a property filter |
| “Which companies were most active this month, and which of them are on the Pro plan?” | list_group_types, list_groups, group_profile |
| “Which events do the most companies use, and how many events carry no company?” | event_breakdowns with count_by |
| “What share of companies that start a trial reach their first invite?” | list_funnels, funnel_results with count_by |
Adding “cite the provenance” or “stop if the data is degraded” to a prompt keeps an agent honest about what it actually read.
Dates and filters
startandendareYYYY-MM-DDdates. Send both or neither. The range is inclusive,endcan’t be beforestart, and a window is at most 3,660 days.- Without dates, a tool reads the last 30 days including today, in the project’s reporting time zone (Settings › General).
overview, retention, list_events and event_breakdowns also take report filters: a sourceId, a saved segmentId, and up to 20 propertyFilters, each with up to 100 values.
operatorisis(the default),is_not,contains,not_contains,is_set,is_not_set,gt,ltorbetween.is_setandis_not_settake no values,gtandltone, andbetweentwo (inclusive). Bounds are numbers orYYYY-MM-DDdates.containsignores case.applyToismatching-events(only events that match count) orpeople-ever-matched(everything from people who ever matched).- Every ID is resolved inside the key’s project.
{
"start": "2026-08-01",
"end": "2026-08-31",
"sourceId": "src_…",
"segmentId": "seg_…",
"propertyFilters": [
{ "property": "plan", "operator": "is", "values": ["pro"], "applyTo": "matching-events" },
{ "property": "amount_cents", "operator": "gt", "values": ["5000"], "applyTo": "matching-events" }
]
}Provenance
Every successful call returns its answer twice: structuredContent with data and provenance for programs, and a text version, with the same provenance, for the model reading it.
{
"data": { … },
"provenance": {
"window": { "start": "2026-08-25", "end": "2026-08-31", "timeZone": "Europe/London" },
"filters": { "interval": "weekly" },
"scope": {
"organizationId": "org_…",
"projectId": "prj_…",
"label": "organization org_… / project prj_…"
},
"freshness": { "dataAsOf": "2026-08-31T21:14:03.000Z", "cache": "not-exposed" },
"degraded": false,
"reason": null,
"definitionHash": "…"
}
}windowis the dates that were read, with thetimeZonewhen the project doesn’t report in UTC.filtersrecords every accepted argument other than the dates, including defaults such asinterval.scopealways comes from the verified key.freshness.dataAsOfis the newest timestamp in the answer.cacheisnot-exposed: ClickClacks doesn’t guess whether a result was cached.definitionHashidentifies the saved definition behindlist_funnels,funnel_results,retentionandlist_segments.
When a report can’t be read, degraded is true, reason says what’s missing, and the numbers come back as null. Saved definitions, such as a funnel’s steps, can still come back. Treat a degraded answer as unknown, never as zero.
Agency keys
An agency that manages several client organizations can use one key for all of them. Agency owners and admins create agency keys (cka_agency_…) in Agency settings › MCP keys. Agency keys are part of the Pro plan.
- It reads what its creator can open, right now. The key stores no project list; access is checked on every call. Remove the creator from a project and the key stops reading it; remove them from the agency and the key stops working.
- Every tool takes a
projectargument, a slug or ID aslist_projectsreturns it. It’s required unless the key can read exactly one project. list_projectslists the projects the key can read, with slug, ID, time zone, source count and events this month.compare_projectscomparesvisitors(the default) oreventsacross projects over the same calendar dates, each in its own time zone, sorted high to low. A total is given for events only, since visitors don’t add up across projects.- Every call is logged per project it read, and the key list shows each key’s calls over the last 30 days. Any agency owner or admin can revoke any agency key.
Rate limits and errors
- Each key can make about 120 requests a minute. The limit is approximate: it’s counted per server instance, not globally.
- Responses carry
X-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Reset. - Over the limit, a call gets the JSON-RPC error
-32029withretryAfterSeconds, and aRetry-Afterheader. - Bad arguments get
-32602, an unknown method-32601, and a report that can’t run-32603. An agency key whose agency isn’t on a plan with agency keys gets-32003. - Responses are sent with
Cache-Control: private, no-store.
Next steps
- Glossary: what funnels, segments and friction findings are.
- MCP feature page: a conversation, start to finish.
- Retention and deletion: how long the data an agent reads is kept.