This repo has one purpose: to master and serve ground truth StackQL queries.
Every entry is treated as authoritative by AI agents (via the stackql MCP
server's query_library_search and query_library_get tools) and by humans
browsing stackql.io/docs/query-library.
New queries are the contribution we want most. If you have a StackQL query that answers a real question - an inventory sweep, a security posture check, a lifecycle operation - turn it into an entry and open a PR. The bar is correctness, not volume: one verified query beats ten guessed ones. Wrong field or resource names poison agent retrieval downstream, so only submit queries you have verified against the provider docs or a live stackql instance.
Contributions are attributed: add the optional author and/or
author_company front matter fields and the published query page credits you
("Contributed by") - see the authoring rules below.
Each entry is one Markdown file with YAML front matter under
query-library/queries/. A build step compiles all entries into the
artifacts the MCP server fetches (index.json, manifest.json, per-query
.json and .md files) under static/docs/query-library/. Generated
artifacts are committed so the raw GitHub fallback tier works without a site
deploy, and the Docusaurus site in this repo renders the same entries as the
human surface.
The id of an entry is its path under query-library/queries/ without the
.md extension, and it must have exactly three segments:
family/service/slug, for example aws/ec2/regions-enabled. Segments are
lowercase a-z0-9_-.
The first segment is the provider family - the brand users and agents think
of, not necessarily a literal StackQL provider name. aws holds entries for
both the aws and awscc providers; databricks holds entries for
databricks_account and databricks_workspace. Front matter providers
names the actual StackQL provider(s) the template references; validation maps
providers[0] to its family (map in src/configs/provider-families.json,
shared with the site components) and requires it to match the directory.
Extend that map when a new multi-provider family arrives.
Ids are permanent. They are cached by MCP servers and referenced by related
lists. To rename an entry, add the new file and flip the old one to
status: deprecated with a note pointing at the replacement. Never delete or
move a published id.
- Copy
query-library/templates/query-template.mdtoquery-library/queries/<family>/<service>/<slug>.md. - Fill in the front matter.
query-library/schema/front-matter.schema.jsonis the source of truth for every field; the main rules are:verbroutes execution:select,mutationorlifecycle.intent_keywordsis the primary retrieval field. Write them exactly as users phrase asks ("list all s3 buckets", "is my bucket public").- Every
{{placeholder}}in the template must be declared inparams, and every declared param must appear in the template. - Params without a
defaultneed anexample; CI uses it to render the template for the parse check. permissions(optional) lists the provider-native authorization actions the template's wire calls require - distinct fromauth, which names the credential env vars for identity. Use the provider's own IAM syntax exactly: AWSservice:Action, AzureMicrosoft.<RP>/<type>/<verb>, GCPservice.resource.verb. Declare only what the calls actually need (this feeds least-privilege policy generation, so over-declaration defeats its purpose), and when unsure omit the field entirely - a wrong permission list is worse than none, because agents relay it verbatim to operators as 403 remediation guidance. AWS actions usually mirror the underlying API operation (ListUsers ->iam:ListUsers); entries that go through Cloud Control (theawsccprovider) need both thecloudformation:*action for the wire call (cloudformation:ListResources,cloudformation:GetResource,cloudformation:UpdateResource, ...) and the underlying service actions - Cloud Control API authorizes under thecloudformation:prefix.authorandauthor_company(optional, either or both) credit the contributor: they render as a "Contributed by" row on the query page and are carried in the emitted JSON.authoris a personal display name,author_companyan organization name.- Do not add an
idkey. The id is derived from the file path (idis a reserved Docusaurus front matter key).
- Write the body:
- one intro paragraph
- a
## Querysection whose first```sqlfence is the template - an optional
## Notessection (compact prose; it becomes thenotesfield in the emitted JSON)
- New entries start as
status: draft. Flip tostableonce the query has been executed against a live provider (record the date inlast_verified).
pip install -r query-library/scripts/requirements.txt
# structural validation (schema, placeholder parity, ids, related refs)
python query-library/scripts/validate.py
# regenerate the published artifacts (run before committing)
python query-library/scripts/build-artifacts.pyCommit the regenerated files under static/docs/query-library/ together with your
entry. CI fails the PR if the committed artifacts do not match the sources.
The repo ships a .pre-commit-config.yaml that runs the same validation and
artifact-freshness checks at commit time, so you find out in seconds instead
of at CI. One-time setup per clone:
pip install pre-commit
pre-commit installThe hooks only fire when a commit touches query-library/ or the committed
artifacts. On a freshness failure the hook has already regenerated the
artifacts in your working tree - review them, git add them, and commit
again. The hooks install their own Python dependencies, and CI enforces the
same checks regardless, so skipping this setup just means slower feedback.
Per PR:
- schema validation and placeholder/param parity in both directions
- unique ids and titles,
relatedids resolve - template render check with the stackql binary (no execution)
- committed artifacts under
static/docs/query-library/are up to date
Nightly:
- read-only (
verb: select) stable entries are executed against sandbox credentials;last_verifiedis updated on success - failing entries are flipped to
status: draftand an issue is opened - aspirational: run the read-only subset under a role built from each entry's
declared
permissionsrather than a broad read-only role, turning the declaration into a tested claim (a 403 then means the declaration is incomplete and flags the entry, same as a functional failure)
The rendered site generates its browse pages from the built artifacts, so a
new provider or entry appears with no extra step once artifacts are rebuilt
(build-artifacts.py also writes the per-family .mdx stubs - commit them
with the artifacts; the CI freshness gate checks both):
/docs/query-library- landing page with one card per provider family, driven by the generatedproviders.json/docs/query-library/<family>- entry table per provider family (generated<family>.mdxstub underquery-library/)/docs/query-library/queries/<id>- the rendered entry, with a metadata panel (verb, providers, credentials, cost warning, last_verified) and related-entry links driven entirely by front matter
All three levels share the query library sidebar, generated per family
directory by sidebars-query-library.js.
For a proper display name and blurb on a brand-new family's card, add it to
src/configs/providers-data.json keyed by the family id; until then a
capitalized fallback renders. Logos resolve favicon-first from
static/img/providers/<family>/ (favicon.svg, favicon.png, favicon.ico,
then <family>.png); drop a favicon file there to change the mark.
_account/_workspace suffixed ids fall back to the base brand directory.
build_idinmanifest.jsonis a content hash of the compiled library, not the site build id. It changes when and only when library content changes, and MCP servers use it as their cache key.- The site serves the artifacts at
https://stackql.io/docs/query-library/(this repo deploys as its own Netlify site, proxied under that prefix by the main stackql.io site); the raw fallback ishttps://raw.githubusercontent.com/stackql/query-library.stackql.io/main/static/docs/query-library/. Both paths are contract surfaces for deployed MCP servers. Do not move them.