Authentication
Authenticate management requests with a Supabase admin session, and MCP tool calls or HTTP API requests with a scoped mcp_ API key.
Management API authentication
Every endpoint under /admin/mcp (servers, tools, agency access, API keys) requires a Supabase session JWT for an allowlisted admin account. Provide it as a bearer token.
Supabase session JWT for an allowlisted admin account. Format: Bearer <access_token>.
A missing token, an invalid/expired token, or a token for an account not on the admin allowlist returns 401 with { "error": "Unauthorized" }.
Gateway authentication
The gateway at POST /mcp accepts an mcp_ API key created through the management API. Provide it as a bearer token or in the X-Api-Key header.
MCP API key as bearer token. Format: Bearer mcp_....
MCP API key. Alternative to the Authorization header.
The key must begin with mcp_. A missing, malformed, or deactivated key returns HTTP 401 with a JSON-RPC error (code -32600).
Scopes
An mcp_ key carries a scopes array that gates which tools it can call (only tools within the scopes are listed; calling any other tool by name answers insufficient_scope naming the scope to add) — and, when the key is sent as a Bearer to the HTTP API, which routes it may call. Scopes use <family>:<action> form, with * wildcards:
| Form | Example | Meaning |
|---|---|---|
family:read | people:read | Read operations in one family |
family:write | insights:write | Write operations in one family — implies family:read |
family:* | dashboards:* | Any action on one family |
* | * | Everything |
An empty scopes array grants nothing. Unrestricted access is the explicit ["*"]. Keys created without a scopes field default to ["*"].
Families
Families follow the plan features they belong to, so a plan and a key describe access in the same words. Twenty-three families:
| Family | Grants | Actions |
|---|---|---|
people | Person profiles, identities, merges, merge warnings | read |
events | Event volumes, trends, breakdowns, property discovery, Explore queries, tracking coverage, Core Web Vitals | read |
sessions | Session analytics | read |
replays | Session replay timelines | read |
exceptions | Error tracking | read |
heatmaps | Click and scroll heatmaps | read |
bigquery_export | Export status and schema | read |
insights | Saved insights, run and preview | read, write |
dashboards | Dashboards | read, write |
canvases | Canvases | read, write |
segments | Segments, counts, membership | read, write |
constraints | Constraint finder and its benchmarks | read, write |
correlation | Correlation | read |
incrementality | Incrementality experiments and coefficients | read, write |
contracts | Event contracts and violation reports | read, write |
destinations | Destinations, credentials, deliveries | read, write |
settings | Tracking settings and the ingestion on/off switch | read, write |
links | Link redirects, click analytics, link webhooks, uptime | read, write |
tags / snippets / transformers | SDK configuration | read, write |
airbyte | Airbyte sync orchestration — agency-wide keys only | read, write |
agency | Client roster and cross-location rollups — agency-wide keys only | read |
Every tool's exact scope is in the Tool Reference, which is generated from the server's registry. list_clients and select_client need no scope on an agency-wide key.
Key issuance (POST/PATCH /builder/mcp/api-keys) validates every scope string against this catalog and answers 400 invalid_scope — listing the offending strings and every valid one — rather than storing a typo that silently grants nothing. The retired data:* and analysis:* families are rejected the same way; existing keys that held them were expanded to the new families.
A key with a null location is agency-wide (it selects a client per session, or names one per HTTP request); one bound to a location is client-scoped and can never reach an agency-grain operation, whatever its scopes say.
Gateway tools
Gateway (upstream) tools at POST /mcp require <scope_prefix>:call, where <scope_prefix> is the owning server's configured prefix. If the server has no scope prefix, the required scope is upstream:call.
Using an mcp_ key over HTTP
The same mcp_ key is accepted as a Bearer on every /client/v1 and /builder route. It is the scoped credential of the HTTP API: an agency key (ak_) is read-write across the whole agency and a location secret (sk_) is read-only for one location, so an mcp_ key is how you hand an integration exactly one capability.
curl "https://mythic-analytics.gulp.workers.dev/client/v1/data/people?location_id=loc_abc" \
-H "Authorization: Bearer mcp_..."
Each route requires the scope of the tool that proxies it — the Proxies column of the Tool Reference is the mapping. Rules:
- Location binding works as for
ak_/sk_. A client-scoped key carries its own location; an agency-wide key names one withlocation_idorX-Location-Idon location-grain routes (400 location_requiredotherwise). - Writes need the
:writescope, whichever key shape holds it — unlikesk_, a client-scopedmcp_key can write. - Agency-grain routes (
/client/v1/agency/*,/client/v1/airbyte/*,GET /builder/locations) refuse client-scoped keys with403 agency_key_required. - Routes with no tool are closed to
mcp_keys, including an unrestricted one: the OAuth browser legs, the embed SPA's internals, and above all key issuance under/builder/mcpanswer403 not_available_to_scoped_keys. Minting or rescoping a key is the privilege boundary and stays withak_. - A missing scope answers
403 insufficient_scopewith therequiredscope named. - Plan entitlements and per-key rate limits apply as on any other key (
rate_limit_per_minute, default 60).
Never expose admin session tokens or mcp_ API keys in client-side JavaScript, public repositories, or logs. Both are server-side credentials. The raw mcp_ key is shown only once, at creation.