MCP Server

Cohorly exposes its analytics substrate - queries, cohorts, reports, boards, definitions, annotations, feature flags 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, manage feature flags, 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.

The MCP server is one door onto the product, not a separate one. Every tool is a thin wrapper over the same code path the dashboard's REST API calls - same validation, same tenant scoping, same rate limiting posture.

Getting a token

Tokens are minted from the dashboard, not the API. In Settings -> MCP access (organization owners only):

  1. 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.
  2. 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.
  3. 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):

bash
claude 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:

CredentialWhoNotes
MCP tokenAn agent connected on your behalfchrly_mcp_..., minted in the dashboard, scoped to read / write / governance and optionally to one project. Best for CI, scripts and any headless agent that can hold a long-lived secret.
OAuth 2.1 access tokenAn interactive client you clicked "Connect" onShort-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 tokenA signed-in user driving the MCP interactivelySame credential the dashboard itself uses; expires in an hour
Superadmin API keyInternal/support toolingSpans 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

ScopeGrants
readEvery read-only tool and resource: queries, users, cohorts, reports, boards, flags, the data dictionary
writeCreate/update/delete of cohorts, reports, boards, definitions, annotations and feature flags
governanceData-dictionary writes (rename, merge, drop an event or property) - also requires an organization owner credential

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.

Tools

53 tools across ten areas. Every tool is scoped to the project(s) your credential can see; none accepts an organization id.

Discovery

ToolPurpose
list_projectsProjects visible to this credential
get_project_overviewOne-call orientation: stats, recent events, board count
list_eventsEvent catalogue with lifetime/30-day counts
search_eventsFuzzy match a phrase against the full event vocabulary
get_event_propertiesProperty names observed on one event
list_profile_propertiesProfile (user) property names
get_lexiconData-dictionary entries: display names, descriptions, tags
get_collection_healthFreshness, per-event status, SDK sources, rename suggestions
get_usagePlan, event quota, retention window, current usage

Analysis

ToolPurpose
run_queryExecute a full Query IR document - the primary tool
query_trendBuild and run a trend query from flat arguments
query_funnelBuild and run a funnel query from a step list
query_retentionBuild and run a retention query
query_flowsBuild and run a flows (user-paths) query
query_sessionsSession count and average duration over a range
drilldownThe 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

ToolPurpose
list_usersSearch user profiles
get_userOne profile plus its recent events
list_raw_eventsThe raw event stream, filtered, capped at 200 rows

Cohorts

ToolPurpose
list_cohorts, get_cohortList / read saved cohorts
preview_cohortCount and sample without saving a definition
get_cohort_membersThe distinct ids in a cohort
create_cohort, update_cohort, delete_cohortManage saved cohorts

A cohort definition combines property and behavior criteria. Property criteria take these operators: eq, neq, contains, set, not_set (text predicates); gt, gte, lt, lte (numeric - stored values that are not numbers never match); and version_gt, version_gte, version_lt, version_lte (semver - the value is major[.minor[.patch]] compared component-wise, so 1.100.0 counts as greater than 1.57.0; a -prerelease suffix sorts strictly below the same core version, so 1.57.0-beta does NOT satisfy version_gte 1.57.0, while +build is ignored entirely; unparseable values never match). The version operators are how you express "app >= 1.57.0" as one criterion instead of a list of contains prefixes. On the SDKs the version-shaped property is $ios_app_release on iOS ($ios_app_version is the build number) and $android_app_version on Android.

Reports

ToolPurpose
list_reports, get_reportList / read saved reports
run_reportExecute a saved report, resolving any relative date range
create_report, update_report, delete_reportManage saved reports

Boards

ToolPurpose
list_boards, get_boardList / read boards and their cards
create_board, update_board, delete_boardManage boards
create_starter_boardSeed a starter board of common reports and cohorts

Definitions & annotations

ToolPurpose
list_definitions, create_definition, update_definition, delete_definitionDerived events and calculated metrics
list_annotations, create_annotation, update_annotation, delete_annotationReleases, campaigns and incidents on the timeline

Governance

ToolPurpose
update_lexicon_entryEdit a data-dictionary entry; rename/merge/drop is owner-only

Feature flags

ToolPurpose
list_flags, get_flagList / read flags, with variants and targeting rules (includeArchived opts archived flags into the list)
create_flagCreate a flag: key, name, type, variants (rolloutPct must sum to 100), rules
update_flagEdit metadata and/or rules - replace_rules, add_rule, remove_rule
archive_flag, revive_flagArchive (stops evaluating, keeps history) and bring back; both idempotent

A flag rule carries a cohortId (property-based targeting is always a cohort reference - there are no inline property predicates on a rule), an optional distinctIds override list (max 50, unconditional: rolloutPct 100 and no cohort), a rolloutPct, and an optional variant key. Pair a flag rule with a cohort using the version_* operators to target by app version, for example "everyone on 1.57.0 or newer".

Resources

Four read-only resources an agent can read without a tool call:

URIContent
cohorly://query-irThe Query IR authoring guide - document shape, the four analysis kinds, date ranges, filters, breakdowns, and worked examples
cohorly://projectsProjects visible to the credential
cohorly://projects/{projectId}/vocabularyEvent, data-dictionary and cohort vocabulary for one project
cohorly://projects/{projectId}/boardsBoards 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).

GET/api/mcp/tokens

List this organization's MCP tokens. Never returns the secret, only its name/prefix/scopes.

POST/api/mcp/tokens

Mint a new MCP token. Organization owner only, for any scope.

NameTypeRequiredDefaultDescription
namestringRequired-

1-80 characters.

scopes("read" | "write" | "governance")[]Optional-

Defaults to ['read'].

projectIdintegerOptional-

Pin the token to one project. Omit for an org-wide token.

expiresInDaysintegerOptional-

1-365. Omit for a token that never 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.

DELETE/api/mcp/tokens/{id}

Revoke a token. Organization owner only. Takes effect immediately.

bash
curl 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": null,
"lastUsedAt": 1753900800000,
"revokedAt": null,
"createdAt": 1753000000000,
"createdByEmail": "owner@example.com"
}
]
}
bash
curl -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": null,
"lastUsedAt": null,
"revokedAt": null,
"createdAt": 1753900800000,
"createdByEmail": "owner@example.com"
}
}
bash
curl -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.
  • read still reads personal data. Profiles and distinct ids are reachable through list_users/get_user under the read scope, exactly like the equivalent dashboard screens - scope a token to read only 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.
  • 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.
PreviousQueries
NextData model