stmo-cli
How it works
stmo-cli is a CLI that Claude Code calls on your behalf. Install it, set your API key, and Claude Code can:
- Explore — discover data sources, find existing queries, browse schemas
- Write — create new Redash queries with proper BigQuery SQL
- Deploy — push queries, charts, and dashboards to STMO
- Execute — run queries and inspect results
- Analyze — export data for deeper analysis with other tools
For example, ask Claude Code to:
- "Find queries about Firefox DAU"
- "Write a query to track [metric] over time"
- "Fetch and run query #12345"
- "Explore what telemetry tables are available"
Pair it with the mozdata plugin for telemetry expertise and probe discovery.
Prerequisites
- Redash API key from https://sql.telemetry.mozilla.org
Installation
For people who already have the Firefox source stored locally
cd /path/to/your/firefox/source/folder
./mach bootstrap
For anyone else
Install cargo-binstall.
cargo binstall stmo-cli
Build from source:
# The binary will be at ./target/release/stmo-cli
Setup
-
Get your Redash API key from your user profile
-
Provide the key:
On macOS, run this once in your own terminal (not through Claude Code, which has no terminal to prompt in):
This stores the key encrypted in the macOS Keychain (service stmo-cli) and reads it
back automatically from then on — every Claude Code session, every worktree, no env var,
no manual export. The first stmo-cli login (or the first command run with no key set,
if you're in a terminal) prompts you for the key with a hidden security prompt; grant
"Always Allow" once when macOS asks, and later reads are silent.
On other platforms, or if you'd rather manage it yourself, set the environment variable directly:
# optional, this is the default
REDASH_API_KEY always takes precedence over the Keychain when set.
For Mozilla, the key can be accessed via the following URL: https://sql.telemetry.mozilla.org/users/me
- Create directories:
init refuses to scaffold into a directory that already contains unrelated files (e.g.
your home directory) — pick an empty or dedicated subdirectory instead.
init is an interactive wizard — it needs a real terminal, and asks before touching
anything:
? Initialize a git repository? [y/N]
? Create an initial commit? [y/N] (only asked if you said yes above)
? Add linter configs (.sqlfluff, .yamllint)? [Y/n]
? Install pre-commit hooks? [y/N] (only asked if git + linters + pre-commit are all available)
? Add CLAUDE.md for AI assistants? [Y/n]
queries/ and dashboards/ are always created; everything else is opt-in. init never
runs git on your behalf unless you say yes — no more surprise commits.
- Discover available queries:
- Fetch specific queries:
Usage
Fetch Queries from Redash
This creates/updates:
queries/{id}-{slug}.sql- Query SQLqueries/{id}-{slug}.yaml- Query metadata (parameters, visualizations, etc.)
Deploy to Redash
Bare deploy compares each tracked query's local .sql/.yaml against the server before
pushing — no git required, and re-running it after a successful deploy pushes nothing.
Warning: This force overwrites the queries in Redash. Your local files are the source
of truth for what gets pushed; putting them under version control is optional but
recommended (stmo-cli init can set that up for you).
Execute Queries
|
execute ID deploys the local .sql/.yaml first if it differs from what's stored on the
server (SQL, name, data source, or parameters), so it never silently runs a stale copy — then
it always runs the up-to-date server-stored query.
execute --data-source ID runs ad-hoc SQL directly against a data source without creating a
tracked query — useful for one-off exploration. It has no parameter schema, so multi-value
parameters can't be expanded for you — inline the values directly in the SQL (e.g.
IN ('release', 'beta')).
Parameters are passed as --param name=value (repeatable). Values are parsed as JSON when
possible, anything that isn't valid JSON is treated as a plain string.
Manage Query Snippets
Snippets don't have an archive concept in Redash — snippets delete removes the snippet on
the server and deletes the local files in one step; there's no separate --cleanup flag.
File Structure
queries/
├── 123-mobile-crashes.sql
└── 123-mobile-crashes.yaml
dashboards/
└── 456-my-dashboard.yaml
snippets/
├── 31-reviewbot_e2e_action_ctcs.sql
└── 31-reviewbot_e2e_action_ctcs.yaml
Query IDs are embedded in filenames ({id}-{slug}.{ext}), so no separate config file is needed.
Snippet filenames use {id}-{trigger}.{ext} (Redash snippets are keyed by trigger, not name).
Development
Pre-commit Hooks
The project uses clippy in pedantic mode:
Install pre-commit hooks:
Building for Release
Architecture
See CLAUDE.md for detailed architecture documentation.