Microsoft Clarity CLI
- Agents and operations
- Operations CLI
- 2026
- Open source
Go, Cobra, Clarity Data Export API, Clarity MCP endpoints
Microsoft Clarity CLI reads a Clarity project from the terminal. It exports dashboard metrics, asks analytics questions in plain language, samples session recordings and searches the documentation. It ships as one Go binary with named project profiles and table, JSON or YAML output. I wrote it for people and for coding agents, since an agent cannot click through a dashboard.
The constraint that shaped it is a quota. The Data Export API allows ten requests per project per day, covers only the previous one to three days, and returns at most 1,000 rows. A client that verifies tokens in the background, or retries a 429, spends that budget on nothing. Most of the design is about sending only the requests someone asked for.
# Checked locally, before any request spends quotaclarity export --days 4Error: --days must be 1, 2, or 3 for the Export APIclarity export --dimension OS --dimension osError: duplicate dimension "OS"clarity recordings list --count 251Error: --count must be between 1 and 250clarity commands resolve -- auth logout | jq -r .effectlocal_write
4 commands4 lines of output
- Chapter
- Agents and operations
- Category
- Operations CLI
- Year
- Availability
- Open source
- Last commit
- Go
- Cobra
- Clarity Data Export API
- Clarity MCP endpoints
Two backends, one token
Clarity answers on two kinds of endpoint, and both take the same project token as a bearer header. The export is a documented GET. The other three are POSTs that Microsoft’s open-source MCP server sends, which I call directly. Microsoft’s backend interprets the natural language, so no MCP process, Node.js runtime or LLM key is involved.
| Command | Endpoint | Bounds |
|---|---|---|
export | GET www.clarity.ms/export-data/api/v1/project-live-insights | 1 to 3 days, 3 dimensions, 1,000 rows |
analytics query | POST clarity.microsoft.com/mcp/dashboard/query | a question and an IANA timezone |
recordings list | POST clarity.microsoft.com/mcp/recordings/sample | 1 to 250 sampled sessions |
docs search | POST clarity.microsoft.com/mcp/documentation/query | one focused question |
internal/client/client.go refuses redirects, so a token never reaches another host. It decodes with UseNumber, so large identifiers stay exact. A 200 carrying isError, success: false or a nonzero dataErrorType still fails. Error bodies are never echoed, since they can hold credentials.
Checked before sending
Anything a command can check locally is checked before a client exists. export matches dimension names against the nine it supports, and rejects a fourth dimension or a duplicate. recordings list validates filters against a schema mirrored from types.ts, enums included, so Desktop fails where Clarity expects PC. analytics query loads the timezone before asking anything. TestValidationBeforeNetwork in cmd/root_test.go fails if an invalid case even constructs a client. auth login and auth status stay local unless --verify is passed, and the help text says that costs one export request. Nothing retries. A 429 comes back with its Retry-After value, and under -o json the error itself is a JSON object on stderr.
Sampling session recordings
clarity auth login --profile website
clarity auth status
clarity export --days 3 --dimension Device --dimension Country/Region -o json
clarity recordings filters -o json
clarity recordings list --filters-file filters.json \
--start 2026-09-01 --end 2026-09-08 --device Mobile --rage-clicks \
--count 50 --sort SessionDuration_DESC -o json
Login takes the token from a hidden prompt, stdin or CLARITY_API_TOKEN, and saves it without a request. recordings filters prints the accepted fields offline. Explicit flags override the matching fields of the filters file, and --rage-clicks=false asks for false rather than for nothing. The client sends the dates twice, at the top level and under filters.date, and turns the sort name into the numeric index the backend expects. What comes back is a sample of metadata, links and timelines, never a population and never a video.
Decisions and costs
- Call the MCP backend instead of running the MCP server. One binary covers all four surfaces. The cost is an unversioned contract, so keeping request bodies aligned with the upstream source is my job.
- One token per project, in
~/.config/clarity-cli/config.yamlwith mode 0600 on Unix and locked, atomic writes. Clarity documents no account-wide API login. The file is plaintext, and a setCLARITY_API_TOKENoutranks the selected profile. - Make environment safety sticky.
--read-only=falsecannot undoCLARITY_READ_ONLY=true, whichcmd/sticky_safety_test.gopins, so an agent runner can rely on it. - The one I would revisit:
commands resolvederives an effect from command annotations, andupdatecarries none. It reportsreadforupdate --yes, which replaces the binary. Read-only mode still blocks the install inside the command, but the resolver should not need that backstop.
Where it stops
It is an independent client and not affiliated with Microsoft. Project and team administration and the browser tracking APIs are out of scope, since tracking runs inside an instrumented site. Exports have no pagination. Recordings carry no video or heatmap image, and a longer lookback cannot extend Clarity’s retention. There is no token refresh, and auth logout removes only the local copy. Reckon runs the binary with CLARITY_READ_ONLY and CLARITY_NO_INPUT set, and counts export requests per connection. clarity update verifies a checksum before replacing itself, and reads the releases/latest redirect rather than the rate-limited GitHub API. It builds with Go 1.27, is MIT licensed and ships macOS, Linux and Windows binaries.