MCP Server
Cohorly exposes its analytics substrate - queries, cohorts, reports, boards, definitions, annotations and the data dictionary - to AI agents over the Model Context Protocol. One endpoint (POST /mcp), Streamable HTTP, no SQL access, and the same query pipeline every dashboard screen uses.
The MCP server lets an AI agent - Claude Code, another MCP-speaking client - act as a product analyst against your own data: it can run trends, funnels, retention and flows, look up event and property names, build and save cohorts, reports and boards, and read your data dictionary. It runs no hand-written SQL and answers no analysis outside the same Query IR pipeline every chart in the dashboard already uses, so an agent can see exactly what the dashboard sees and nothing more.
Getting a token
Tokens are minted from the dashboard, not the API. In Settings -> MCP access (organization owners only):
- Click Create token, give it a name, pick one or more scopes
(
read/write/governance), and optionally pin it to a single project or set an expiry. - Copy the token. It is shown exactly once, in the form
chrly_mcp_<43 characters>- the server stores only its hash, so there is no way to retrieve it again later. If you lose it, revoke it and mint a new one. - Revoking a token (from the same table) takes effect immediately: the next request with that token is refused, nothing is cached.
Only an organization owner can mint or revoke a token, for any scope
including read.
Connecting
With the Claude Code CLI(opens in new tab):
bashclaude mcp add --transport http cohorly https://cohorly-service.velloalabs.com/mcp \--header "Authorization: Bearer chrly_mcp_..."
Any MCP client that speaks Streamable HTTP can connect the same way: point
it at POST https://cohorly-service.velloalabs.com/mcp with an
Authorization: Bearer <token> header. The server negotiates protocol
version automatically and needs no separate initialize handshake for
clients on the latest spec.
Claude Desktop and claude.ai
Add Cohorly as a custom connector pointed at
https://cohorly-service.velloalabs.com/mcp. Unlike the CLI snippet above,
you don't paste a token: the client discovers Cohorly's OAuth support
automatically, opens a browser to a consent screen on the dashboard, and
comes back with access already granted. Expect a sign-in prompt (if you
aren't already signed in) followed by a screen asking which organization and
project to connect, with Allow / Deny buttons.
Authentication
Four credentials are accepted on Authorization: Bearer, the same header
every request uses:
| Credential | Who | Notes |
|---|---|---|
| MCP token | An agent connected on your behalf | chrly_mcp_..., minted in the dashboard, scoped to read / write / governance and optionally to one project. Expires (90 days by default, at most 365) and never carries more than its creator's current role allows; it stops working if its creator leaves the organization. Best for CI, scripts and any headless agent. |
| OAuth 2.1 access token | An interactive client you clicked "Connect" on | Short-lived (15 minutes), issued after you approve a consent screen. Used by Claude Desktop and claude.ai's connector UI, which discover it automatically - see "Claude Desktop and claude.ai" above. Manage what you've connected from Settings -> Connected apps. |
| Firebase ID token | A signed-in user driving the MCP interactively | Same credential the dashboard itself uses; expires in an hour |
| Superadmin API key | Internal/support tooling | Read-only on the MCP. Spans every organization; every tool call must then name a projectId explicitly |
A request with no valid credential gets 401 with a WWW-Authenticate: Bearer header naming the problem and pointing at Cohorly's OAuth discovery
document, which is how an OAuth-capable client finds its way to the consent
screen without you configuring anything.
Scopes
| Scope | Grants |
|---|---|
read | Every read-only tool and resource: queries, users, cohorts, reports, boards, the data dictionary |
write | Create/update/delete of cohorts, reports, boards, definitions, annotations and feature flags. Deletes and flag changes that affect production wait for a human approval (see below) |
governance | Data-dictionary writes (rename, merge, drop an event or property) - merge and drop also require an organization owner, and wait for an owner's approval |
A tool call missing a required scope returns a normal tool result with
isError: true naming the missing scope - never a protocol-level failure,
so an agent can read the message and explain it to you.
Human approval
An agent never gets the final say on a change you cannot undo, or on one your own users would notice. These calls are held instead of executed:
- every
delete_*tool (even withconfirm: true); update_lexicon_entrywhen it drops or merges an event - dropping makes ingestion reject that event's new data, which is lost, not hidden;create_flagfor an active flag,update_flagwhen it turns a flag on or off or changes its variants or rules,archive_flagandrevive_flag;update_cohortwhen it changes who is in a cohort a feature flag targets.
The tool answers with an error that starts with NOT EXECUTED, a summary of
the change and a link to /mcp/approve in the dashboard. Anyone in your
organization can review it there and choose Approve and run or
Reject; drops and merges need an owner. Approval runs the exact request
with the approver's permissions, re-checking everything against the data as it
is at that moment - a flag or cohort that was edited after the request makes
the approval fail rather than apply a change you did not review. Pending
approvals expire after 24 hours and are also listed
under Settings -> Pending MCP approvals. The agent can follow the outcome
with get_pending_action, but no MCP credential can approve anything.
Why: an agent reads text your users and your apps send in - event names,
property values - and that text can be written to look like an instruction.
A required confirm: true does not help, because the same agent fills it in.
Every MCP tool call, allowed or refused, is also recorded. Organization owners can review the trail under Settings -> MCP activity.
Tools
54 tools across ten areas. Every tool is scoped to the project(s) your credential can see; none accepts an organization id.
Discovery
| Tool | Purpose |
|---|---|
list_projects | Projects visible to this credential |
get_project_overview | One-call orientation: stats, recent events, board count |
list_events | Event catalogue with lifetime/30-day counts |
search_events | Fuzzy match a phrase against the full event vocabulary |
get_event_properties | Property names observed on one event |
list_profile_properties | Profile (user) property names |
get_lexicon | Data-dictionary entries: display names, descriptions, tags |
get_collection_health | Freshness, per-event status, SDK sources, rename suggestions |
get_usage | Plan, event quota, retention window, current usage |
Analysis
| Tool | Purpose |
|---|---|
run_query | Execute a full Query IR document - the primary tool |
query_trend | Build and run a trend query from flat arguments |
query_funnel | Build and run a funnel query from a step list |
query_retention | Build and run a retention query |
query_flows | Build and run a flows (user-paths) query |
query_sessions | Session count and average duration over a range |
drilldown | The users behind one point of a prior result |
Every query_* wrapper returns the Query IR document it built alongside the
result, so it can be copied, edited and replayed through run_query or saved
as a report - see cohorly://query-ir for the document shape.
Users
| Tool | Purpose |
|---|---|
list_users | Search user profiles |
get_user | One profile plus its recent events |
list_raw_events | The raw event stream, filtered, capped at 200 rows |
Cohorts
| Tool | Purpose |
|---|---|
list_cohorts, get_cohort | List / read saved cohorts |
preview_cohort | Count and sample without saving a definition |
get_cohort_members | The distinct ids in a cohort |
create_cohort, update_cohort, delete_cohort | Manage saved cohorts |
Reports
| Tool | Purpose |
|---|---|
list_reports, get_report | List / read saved reports |
run_report | Execute a saved report, resolving any relative date range |
create_report, update_report, delete_report | Manage saved reports |
Boards
| Tool | Purpose |
|---|---|
list_boards, get_board | List / read boards and their cards |
create_board, update_board, delete_board | Manage boards |
create_starter_board | Seed a starter board of common reports and cohorts |
Definitions & annotations
| Tool | Purpose |
|---|---|
list_definitions, create_definition, update_definition, delete_definition | Derived events and calculated metrics |
list_annotations, create_annotation, update_annotation, delete_annotation | Releases, campaigns and incidents on the timeline |
Governance
| Tool | Purpose |
|---|---|
update_lexicon_entry | Edit a data-dictionary entry; rename/merge/drop is owner-only |
Feature flags
| Tool | Purpose |
|---|---|
list_flags, get_flag | Feature flags with their variants and targeting rules, to correlate a metric move with a rollout |
create_flag, update_flag, archive_flag, revive_flag | Manage flags; changes that affect evaluation wait for a human approval |
Approvals
| Tool | Purpose |
|---|---|
get_pending_action | Status of a held change: pending, executed (with its result), failed, rejected or expired |
Resources
Four read-only resources an agent can read without a tool call:
| URI | Content |
|---|---|
cohorly://query-ir | The Query IR authoring guide - document shape, the four analysis kinds, date ranges, filters, breakdowns, and worked examples |
cohorly://projects | Projects visible to the credential |
cohorly://projects/{projectId}/vocabulary | Event, data-dictionary and cohort vocabulary for one project |
cohorly://projects/{projectId}/boards | Boards in one project, with a card preview |
Prompts
Five ready-made analyst briefs a client can offer as slash commands or
templates: weekly_product_review, funnel_diagnosis, retention_deep_dive,
feature_adoption, and investigate_metric_change. Each tells the model
which tools to call and in what order, and requires it to state the date
range and definition behind every number it reports.
Managing tokens via the API
Token management is a dashboard-account operation - it takes a Firebase ID token from a signed-in organization member, never the superadmin key (there is no single organization for a superadmin credential to manage tokens for).
List this organization's MCP tokens. Never returns the secret, only its name/prefix/scopes.
Mint a new MCP token. Organization owner only, for any scope.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Required | - | 1-80 characters. |
scopes | ("read" | "write" | "governance")[] | Optional | - | Defaults to ['read']. |
projectId | integer | Optional | - | Pin the token to one project. Omit for an org-wide token. |
expiresInDays | integer | Optional | - | 1-365. Defaults to 90 - every token expires. |
The token field is present only in this response. There is no way to
retrieve it again - mint a new token if it is lost. Organizations are capped
at 20 live tokens.
Revoke a token. Organization owner only. Takes effect immediately.
bashcurl https://cohorly-service.velloalabs.com/api/mcp/tokens \-H "Authorization: Bearer $FIREBASE_ID_TOKEN"
json{"tokens": [{"id": 3,"name": "Claude Code - laptop","prefix": "aB3dEf12","scopes": ["read"],"projectId": null,"expiresAt": 1761868800000,"lastUsedAt": 1753900800000,"revokedAt": null,"createdAt": 1753000000000,"createdByEmail": "owner@example.com"}]}
bashcurl -X POST https://cohorly-service.velloalabs.com/api/mcp/tokens \-H "Authorization: Bearer $FIREBASE_ID_TOKEN" \-H "Content-Type: application/json" \-d '{ "name": "Claude Code - laptop", "scopes": ["read"] }'
json{"token": "chrly_mcp_9f2a...redacted...","row": {"id": 4,"name": "Claude Code - laptop","prefix": "9f2aXqZ1","scopes": ["read"],"projectId": null,"expiresAt": 1761868800000,"lastUsedAt": null,"revokedAt": null,"createdAt": 1753900800000,"createdByEmail": "owner@example.com"}}
bashcurl -X DELETE https://cohorly-service.velloalabs.com/api/mcp/tokens/4 \-H "Authorization: Bearer $FIREBASE_ID_TOKEN"
No body.
Security notes
- Tokens are hashed at rest. The server stores a SHA-256 digest, never the plaintext; the dashboard shows the secret exactly once, at creation.
readstill reads personal data. Profiles and distinct ids are reachable throughlist_users/get_userunder thereadscope, exactly like the equivalent dashboard screens - scope a token toreadonly for an agent you trust with that data, and pin it to one project when you can.- Revocation is immediate. Every request re-reads the token row; nothing about a token's validity is cached.
- A token is only as strong as the person who minted it. Its scopes are capped by its creator's current role, and it stops working when its creator leaves the organization.
- The agent does not approve its own changes. Deletes, drops, merges and production flag changes wait for a human in the dashboard (see "Human approval").
- Every call is audited, including refused ones, with the credential, tool, project, arguments and outcome.
- No tool can reach another organization's data. A project id you pass to a tool is re-checked against your credential on every call - a project-scoped token cannot be redirected to another project, and an org-wide credential cannot reach another organization's project, even by guessing an id.
- Rate limits are per credential, not shared across your organization: 240 read calls and 60 write calls per 60-second window by default, so one runaway agent cannot exhaust everyone else's access.
- OAuth connections revoke instantly and completely. Disconnecting an app from Settings -> Connected apps revokes every token that app was ever issued for that connection, not just its current one - a stolen or replayed refresh token has the same effect automatically.