stmo-cli is a CLI that AI coding assistants such as Claude Code and Codex can use on your behalf. Install it and set your API key. It can then help you:
- 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 or Codex to:
- "Find queries about an example metric"
- "Write a query to track [metric] over time"
- "Fetch and run query #"
- "Explore what telemetry tables are available"
Claude Code users can pair it with the mozdata plugin for telemetry expertise and probe discovery. Codex users can use stmo-cli directly.
- Redash API key from https://sql.telemetry.mozilla.org
cd /path/to/your/firefox/source/folder
./mach bootstrap
Install cargo-binstall.
cargo binstall stmo-cli
cargo build --release
# The binary will be at ./target/release/stmo-cli-
Get your Redash API key from your user profile
-
Provide the key:
On macOS, run this once in your own terminal; the command needs an interactive prompt:
stmo-cli loginThis stores the key encrypted in the macOS Keychain (service stmo-cli) and reads it
back automatically from then on — every assistant 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:
export REDASH_API_KEY="your-api-key-here"
export REDASH_URL="https://sql.telemetry.mozilla.org" # optional, this is the defaultREDASH_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:
stmo-cli init # scaffolds the current directory
stmo-cli init ~/stmo-queries # or scaffolds a directory of your choice (created if missing)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 AGENTS.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.
The generated AGENTS.md is a short stub that points the assistant at stmo-cli guide.
Repos scaffolded before this change keep their long AGENTS.md, and init leaves existing
files alone. To switch, replace that file's contents with the stub, which is the first
paragraph and the Notes section of a freshly generated AGENTS.md.
- Discover available queries:
stmo-cli discover # List your own queries
stmo-cli discover --search "example metric" # Full-text search queries + dashboards- Fetch specific queries:
stmo-cli fetch <query-id> [<query-id>...]stmo-cli fetch --all # Fetch all tracked queries
stmo-cli fetch <query-id> [<query-id>...] # Fetch specific queries
stmo-cli discover # List your own queries
stmo-cli discover --search "example metric" # Full-text search queries + dashboards (--limit, default 50)This creates/updates:
queries/{id}.sql- Query SQLqueries/{id}.yaml- Query metadata (name, parameters, visualizations, etc.)
Saved query filenames use the stable Redash ID, so changing a query's title does not
rename its local files. Existing {id}-{slug}.* files remain supported. fetch and
deploy migrate them to {id}.*: fetch only when their tracked content matches Redash
(divergent files stay untouched and are reported), and a successful deploy writes the
server's resulting copy to {id}.*.
stmo-cli deploy # Deploy queries whose local content differs from what's on Redash
stmo-cli deploy --all # Deploy all queries regardless of differencesBare 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).
stmo-cli execute <query-id> # Run a query (deploys local changes first, if any)
stmo-cli execute <query-id> --param start_date=2026-06-15
stmo-cli execute <query-id> --param channels='["release","beta"]' # Multi-value enum as JSON
stmo-cli data-sources # List data sources
echo 'SELECT 1' | stmo-cli execute --data-source <data-source-id> # Run arbitrary SQL against a data source
stmo-cli execute --data-source <data-source-id> --file scratch.sqlexecute 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.
stmo-cli snippets list # List query snippets from Redash
stmo-cli snippets fetch <snippet-id> [<snippet-id>...] # Fetch specific snippets
stmo-cli snippets fetch --all # Fetch all tracked snippets
stmo-cli snippets deploy # Deploy snippets whose local content differs from what's on Redash
stmo-cli snippets deploy --all # Deploy all snippets
stmo-cli snippets delete <snippet-id> [<snippet-id>...] # Delete snippets in Redash and remove local filesSnippets 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.
queries/
├── {query-id}.sql
└── {query-id}.yaml
dashboards/
└── {dashboard-id}-example-dashboard.yaml
snippets/
├── {snippet-id}-example-snippet.sql
└── {snippet-id}-example-snippet.yaml
Query IDs are embedded in filenames ({id}.{ext}), so no separate config file is needed.
Snippet filenames use {id}-{trigger}.{ext} (Redash snippets are keyed by trigger, not name).
The project uses clippy in pedantic mode:
cargo clippy --all-targets --all-features -- -W clippy::pedantic -D warningsInstall pre-commit hooks:
pip install pre-commit
pre-commit installcargo build --release
./target/release/stmo-cli --helpSee AGENTS.md for detailed architecture documentation.