Databases / BigQuery
Natural language to SQL for BigQuery
NLQueries is an open source natural language to SQL engine for BigQuery. Ask a question in plain English, get SQL that is validated against your real BigQuery schema before it runs, and see the answer.
It works from the command line, as a Python library, and as an MCP server for Claude Desktop, Cursor and other AI assistants.
Connect BigQuery and ask your first question
Install the package, register the connection once, build the knowledge base, and query. Leave out --password and it is prompted for without echoing, then stored in your OS keychain rather than the connectors file. Only where no keychain is available does it go into the stored connection URL, with a warning.
$ pip install "nlqueries-core[bigquery]" $ nlqueries connect bigquery --project-id my-gcp-project --dataset-id my_dataset --service-account-json /path/to/service-account.json --alias bq-prod $ nlqueries process-history bq-prod --annotate $ nlqueries export-kb bq-prod $ nlqueries query bq-prod "how many orders were placed last month by first-time customers?"
How NL2SQL works on BigQuery
Most text-to-SQL tools hand the LLM a schema dump and hope. NLQueries builds a YAML knowledge base from your BigQuery schema and from real query history, in this case the regional INFORMATION_SCHEMA.JOBS_BY_PROJECT view, so it learns which joins your team actually uses and what your columns mean. That knowledge base is what the model sees when it writes SQL.
Every generated statement is parsed with sqlglot and refused unless it is a single read-only query, and its columns are checked against the schema in the knowledge base. It then runs read-only where the engine allows it, with a statement timeout; what each connector enforces has the details per engine. Use nlqueries ask to see the SQL without running it, or nlqueries query to run it. A semantic cache answers repeated questions without another LLM call, and runs the stored SQL again so the rows are current.
BigQuery-specific details
There is no password. Omit --service-account-json and the connector uses Application Default Credentials, which is the natural fit on GKE, Cloud Run or a workstation that has run gcloud auth application-default login.
Outside Google Cloud, --service-account-json also takes a workload identity federation credential configuration, so no service account key is needed. An impersonate_service_account entry runs every query as one narrowly granted service account. See BigQuery authentication.
BigQuery has no transaction at all, so a non-SELECT statement type is logged after the job has run rather than prevented. The grant is the whole defence: roles/bigquery.dataViewer on the datasets you want answered, permission to run query jobs (bigquery.jobs.create, which roles/bigquery.jobUser carries), and nothing that writes.
History is read from one region: the --dataset-id dataset's location, or region-us when none is given. With a dataset set, only jobs that read a table in it are kept. Reading it needs bigquery.jobs.listAll, for example through roles/bigquery.resourceViewer. process-history --days N is honoured.
Set BIGQUERY_MAX_BYTES_BILLED and BigQuery fails a job that would bill more, before it runs. It is unset, so uncapped, by default. See the cost cap.
Full connector notes are in the connectors guide, and the read-only role to create is in database hardening. This connector covers Google BigQuery.
Use it from Claude, Cursor or any MCP client
Run nlqueries mcp-server start and point Claude Desktop, Cursor, or any Model Context Protocol client at it. Its query tool generates SQL through the same pipeline, runs it read-only and returns the rows. Over stdio, the transport desktop clients use, there is nothing to configure. A network transport will not start without authentication, by OIDC or a pre-shared token, and grants per agent and action decide who may run SQL. See MCP authentication.
FAQ
BigQueryFrequently asked questions
How does NLQueries turn a natural language question into SQL?
It retrieves the relevant tables, columns, relationships and past query patterns from a YAML knowledge base built from your schema and, where the engine keeps one, your query history. It asks an LLM for SQL with that context, checks the SQL with sqlglot against a read-only statement policy and the schema in the knowledge base, and runs it read-only where the engine allows, with a statement timeout.
Is the generated SQL safe to run against production?
Every statement is parsed and refused unless it is a single read-only query, then run read-only where the engine allows it, with a timeout and result caps. nlqueries ask shows the SQL without running it. What each engine enforces differs, and on some the database grant is the only control, so pair it with a least-privilege role; the database hardening guide has one per engine.
Which IAM roles does the NLQueries service account need for BigQuery?
roles/bigquery.dataViewer on each dataset it should answer from, and permission to run query jobs in the project (bigquery.jobs.create, carried by roles/bigquery.jobUser). Reading query history also needs bigquery.jobs.listAll, for example through roles/bigquery.resourceViewer.
Can Claude Desktop or Cursor query this database through NLQueries?
Yes. nlqueries mcp-server start runs a Model Context Protocol server. Its query tool generates SQL through the same pipeline, runs it read-only and returns the rows, so any MCP client can ask questions as a native tool call.
Other engines: Postgres · MySQL · Snowflake · Redshift · SQL Server · DuckDB · SQLite