Safe natural-language-to-SQL: validated, read-only queries across many SQL engines.
Project description
askql — governed natural-language data access, for humans and AI agents
Turn a plain-English question into a validated, read-only SQL answer against your real database — and get back not just rows, but proof the query was correct, authorized, and defensible. Self-hosted. Nothing leaves your perimeter. Any SQL engine.
Correct and defensible. Most tools optimize one: a raw LLM writes plausible SQL (often wrong, always ungoverned); a BI tool is governed but can't answer a novel question. askql does both — it grounds the question in your data's real meaning, and it can't be talked into running anything unsafe.
Ground · Govern · Prove
Three things happen on every request. All three ship today.
- Ground — understand what you're asking. A per-database graph of your schema with join paths proven against the data (not guessed from names), business-term glossary, and the real values your columns hold. This is what turns "how many active customers" into the right SQL instead of a confident wrong one.
- Govern — guard the execution. Every query is parsed and checked by an AST validator (the
single source of truth): read-only, no DDL/DML, no
SELECT *on a bare table, sensitive columns blocked, a row cap and timeout enforced — then run through a least-privilege, rolled-back connection. The safety is structural: an agent can't prompt-inject its way past it. - Prove — record what happened. Every query returns a decision envelope — what was decided
(allow / allow-with-masking / deny), which controls applied, which tables and sensitive columns
were touched, and an
evidence_idthat matches the audit log. Every answer is auditable.
30 seconds
pip install "askql[postgres]" # or [oracle] [mssql] [redshift] [jdbc] … + [mcp] [llm]
askql init --dialect postgres # scaffold config/ + docs/ + .env (auto-discovered from here)
# edit .env + config/databases.yaml # credentials + host:port/db + schemas
askql doctor # 10-point connectivity/health check
askql test-db # per-dialect smoke battery (incl. read-only check)
# build the grounding — with join paths PROVEN against the data:
askql scrape --include-views --build-graph --infer-fks --verify-edges --sample-values
askql learn ./saved_sql --examples # (optional) mine real SQL for join paths + examples
askql model validate # is the grounding internally consistent?
# ask — with the reasoning and the governance decision exposed:
askql ask "how many active customers are there" --explain
--explain shows the question coverage, the tables selected, which glossary terms / examples fired,
and the governance decision + evidence_id. No LLM key? Generation falls back to your IDE agent
(Copilot / Claude Code) — the safety pipeline is identical.
Windows: if the
askqllauncher ever errors,python -m askql …is the shim-free equivalent.
Your AI agent is the UI
askql ships an MCP server — seven governed, read-only tools an agent (Claude Desktop, Cursor,
VS Code, or any MCP client) drives directly: list_tables, describe_table, compress_schema,
validate_sql, query, check_freshness, ask.
pip install "askql[mcp]"
askql-mcp # stdio server; point your MCP client's command here
# (bound to one database + identity via T2S_DATABASE / T2S_MCP_USER)
Every tool runs through the same validator + RBAC + read-only executor + audit as the CLI, and
query/ask return the decision envelope. So an agent can explore the schema and run SQL — and
cannot run a DELETE, exceed its row cap, or touch a schema its identity isn't scoped to. The
mcp SDK is a lazily-imported opt-in extra: orgs that disallow MCP can ignore it entirely.
How it's different
| raw LLM agent | traditional BI tool | bare semantic layer | askql | |
|---|---|---|---|---|
| Answers a novel NL question | ✅ (often wrong) | ❌ | ❌ | ✅ grounded |
| Knows your business definitions | ❌ | in-tool only | schema only | ✅ glossary + values + rules |
| Join paths proven against the data | ❌ | ❌ | ❌ | ✅ --verify-edges |
| Unsafe SQL blocked before execution | ❌ | n/a | ❌ | ✅ AST validator |
| Survives prompt injection | ❌ | n/a | ❌ | ✅ structural, not prompt-based |
| Every answer carries provable evidence | ❌ | ❌ | ❌ | ✅ decision envelope + audit |
| Works through your agents (MCP) | ✅ | ❌ | ❌ | ✅ |
| Runs on your real DB, real dialect | ✅ | ✅ | definitions only | ✅ Oracle/MSSQL/Redshift/PG/… |
| Open, reviewable, git-native context | ❌ | ❌ | partial | ✅ plain YAML/MD |
| Self-hosted; no data leaves your perimeter | depends | ✅ | ✅ | ✅ |
The grounding is yours — reviewable, versioned, in git
Everything that makes answers accurate is plain files you own, not state locked in a UI:
schema-graph.<db>.json, glossary.<db>.yaml, examples.<db>.yaml, corrections.<db>.yaml,
docs/domain-rules.<db>.md, synonyms.yaml. Review changes in a PR; roll them back like code.
askql model # one summary of every grounding layer for a database
askql model validate # catches silent accuracy-killers (0-edge graph, glossary→missing table…)
askql model explain "vip" # how a term resolves — glossary hit? column? a real sampled value?
The grounding improves with use: askql feedback captures a confirmed example or a correction
("‘active’ means status='A', not 'ACTIVE'"), and askql learn turns your existing SQL into
proven join paths and labelled examples.
Safety is measured, not asserted
askql eval # 32-case adversarial red-team; exits non-zero on any escape
askql eval --accuracy --cases cases.json # score generated SQL by result-match against gold answers
- Only
SELECT(+UNION/INTERSECT/EXCEPT). No DDL/DML or procedural code — even hidden in a CTE. - No
SELECT *on a bare table/view; explicit columns (COUNT(*)is fine). Row limit required + capped. - No system schemas, no sensitive columns (SSN, PASSWORD, …), no dangerous functions.
- Read-only session, rollback, timeout, and an audit record — on every query.
The red-team is a CI gate (safety is a test that fails the build on a regression). The accuracy harness gives you a number for your own question set. A full evaluation protocol you can hand to a reviewer is in docs/partner-verification.md.
Bring your own model — any provider
Generation is pluggable; the safety pipeline is identical regardless. Set T2S_LLM_PROVIDER:
anthropic · bedrock (Claude on AWS) · vertex (Claude on GCP) · openai · azure-openai ·
openai-compatible (vLLM / Ollama / LM Studio / any self-hosted, works air-gapped) · custom
(your gateway) · unset → BYO-LLM (your IDE agent generates; no backend key). Nothing is locked
to one vendor, and no question or schema is sent anywhere you didn't configure.
Also available
- REST API (
askql-api) — run askql as a service so clients hold no drivers, JVM, or credentials; they call HTTP and get the same validator + RBAC + audit./validate,/compress,/query,/ask. - Docker image — bundles a JRE so the universal JDBC transport works with no local Java/native
drivers. Mount
config/,.env, and vendor jars at runtime. - Python library —
from askql import validate, compress, execute_sql_text, ask— typed (py.typed), optional deps imported lazily, everything config-injectable.
from askql import validate, ask, Settings
r = validate("SELECT id, name FROM app.users LIMIT 10", settings=Settings(dialect="postgres"))
assert r.ok, r.errors
result = ask("how many active users this week") # needs an LLM provider configured
print(result["sql"], result["decision"]["evidence_id"])
Access control (optional)
With an identity present ($T2S_USER / --user) and config/access-control.yaml, a role decides
which environments, schemas, PII mode (blocked / masked / full), row cap, and
rate limit apply — enforced by the executor and surfaced in the decision envelope. With no
identity it's single-user read-only mode. pii=masked redacts sensitive values (***) at the
executor — the model can't reveal what it never receives.
Docs
| docs/partner-verification.md | Reproducible evaluation: connect → safe → correct → defensible |
| ARCHITECTURE.md | Design + the security hardenings over the source playbook |
| CLAUDE.md | Operating instructions for the AI agent driving askql |
| SHIPPING.md | Package / ship / install (wheel · internal index · Docker) |
| CHANGELOG.md | Release history |
Read-only by construction. askql executes only read-only SELECT through a least-privilege
connection; point it at whatever environment your access policy permits. MIT licensed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file askql-0.20.1.tar.gz.
File metadata
- Download URL: askql-0.20.1.tar.gz
- Upload date:
- Size: 121.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b205cbeb7bae886d4300b6081c2cfce150e761144768031459da68ebd411145f
|
|
| MD5 |
6bfc43f8209c3c40de69d34855f9971e
|
|
| BLAKE2b-256 |
94a90adca5e12fb59e4b248adaa4ef13b5b9a8857e46e6bb5c84093b8fc4dd35
|
Provenance
The following attestation bundles were made for askql-0.20.1.tar.gz:
Publisher:
release.yml on TestAutomationArchitect/askql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
askql-0.20.1.tar.gz -
Subject digest:
b205cbeb7bae886d4300b6081c2cfce150e761144768031459da68ebd411145f - Sigstore transparency entry: 2201684707
- Sigstore integration time:
-
Permalink:
TestAutomationArchitect/askql@d059891ef613466aa410ac2f1332ddcb010309cc -
Branch / Tag:
refs/tags/v0.20.1 - Owner: https://github.com/TestAutomationArchitect
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d059891ef613466aa410ac2f1332ddcb010309cc -
Trigger Event:
push
-
Statement type:
File details
Details for the file askql-0.20.1-py3-none-any.whl.
File metadata
- Download URL: askql-0.20.1-py3-none-any.whl
- Upload date:
- Size: 141.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f608cccf34aaf83c28c263d3b13a111a7cbca0cd5ac0af6ba902cb8d2ee3d88
|
|
| MD5 |
11e2459902f6d6aa7b353caeb4374ff0
|
|
| BLAKE2b-256 |
ec981678644a9f5dcd951907a5cb25e084d46d9fdf6eeec372f95e90c1f80dcf
|
Provenance
The following attestation bundles were made for askql-0.20.1-py3-none-any.whl:
Publisher:
release.yml on TestAutomationArchitect/askql
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
askql-0.20.1-py3-none-any.whl -
Subject digest:
6f608cccf34aaf83c28c263d3b13a111a7cbca0cd5ac0af6ba902cb8d2ee3d88 - Sigstore transparency entry: 2201684984
- Sigstore integration time:
-
Permalink:
TestAutomationArchitect/askql@d059891ef613466aa410ac2f1332ddcb010309cc -
Branch / Tag:
refs/tags/v0.20.1 - Owner: https://github.com/TestAutomationArchitect
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d059891ef613466aa410ac2f1332ddcb010309cc -
Trigger Event:
push
-
Statement type: