NL-to-SQL MCP
Provides MCP tools for schema discovery, table metadata retrieval, relevant schema lookup, and safe read-only SQL execution against a SQLite database.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@NL-to-SQL MCPWhat are the top 5 best-selling albums?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
NL-to-SQL — MCP + GraphRAG
Natural-language querying over a SQLite database (the Chinook music-store sample: artists, albums, tracks, genres, customers, invoices, employees, playlists — 11 tables, 11 FK relationships), built around two independent guardrail layers, a schema-graph retrieval step, and an LLM-driven generation pipeline.
Two ways to use this
This repo is actually two separate products sharing the same guardrail and retrieval code:
MCP server (server/main.py) — exposes
list_schemas,get_table_metadata,retrieve_relevant_schema, andexecute_safe_queryas tools for any MCP client (Claude Desktop, Claude Code). In this mode, the connected LLM does its own reasoning about which tables/SQL to use — our code only provides schema discovery, GraphRAG-assisted retrieval, and guardrailed execution. No OpenAI calls happen in this path.Standalone pipeline (pipeline/pipeline.py) — a fully self-contained 5-stage NL-to-SQL system (Plan → Retrieve → Generate → Validate → Execute) that uses the OpenAI API directly. This is what the web app and CLI script call; Claude Desktop is not involved at all in this path.
These don't talk to each other — pick whichever fits how you want to interact with the database.
Related MCP server: MCP Demo — GitHub Copilot + Your Database
Architecture
┌─────────────────────────────────────┐
│ Schema Graph (NetworkX) │
│ tables + columns as nodes, │
│ FKs as edges — built once, │
│ persisted to GraphML │
└───────────────┬───────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌──────────▼─────────┐ ┌────────▼────────┐ ┌─────────▼──────────┐
│ MCP Server │ │ Pipeline (CLI) │ │ Web App (FastAPI) │
│ 4 tools, stdio │ │ scripts/ │ │ web/app.py │
│ Claude Desktop/Code │ │ run_pipeline.py │ │ + static HTML/JS │
└──────────┬───────────┘ └────────┬─────────┘ └─────────┬────────────┘
│ └──────────┬───────────┘
│ │
│ pipeline/pipeline.py
│ Plan → Retrieve → Generate → Validate → Execute
│ │
└──────────────────┬─────────────────┘
│
┌────────────▼────────────┐
│ Guardrail layer │
│ sql_guard.py (AST allow- │
│ list) + read-only conn + │
│ timeout + row cap │
└────────────┬────────────┘
│
db/chinook.db (SQLite)Guardrails
Security is layered, not a single check:
AST-based SQL validation (server/sql_guard.py) — allowlist, not keyword blocklist. Parses generated SQL with
sqlglot; only accepts a singleSELECT/UNION/INTERSECT/EXCEPTstatement, every table reference must exist in the live schema (CTE aliases correctly excluded),load_extension/pragma_*calls blocked, and aLIMITis always injected/clamped to 1000 rows by rewriting the AST — never trusting whatever the caller or LLM wrote.Read-only connection (server/query_executor.py) — SQLite opened via
mode=roURI, an independent backstop at the driver level even if a write somehow passed AST validation.Timeout — a progress-handler wall-clock timeout aborts long-running scans instead of blocking.
Plan-stage scope filtering (pipeline/plan_stage.py) — an LLM call classifies whether a question is in-scope before any SQL generation is attempted. This is a quality/cost filter, not the security boundary — the AST validator is what actually stops unsafe SQL regardless of what Plan decides.
Multi-database support
The web app isn't limited to the bundled Chinook demo. Switch to "Upload your own" and provide either:
a SQLite
.dbfile — used as-is, no conversiona MySQL or Postgres SQL dump (
.sql) — transpiled to SQLite statement-by-statement viasqlglot(web/db_import.py) and imported into a fresh, isolated per-session database
Each upload gets its own session (web/sessions.py): its own SQLite file, its own schema graph,
expiring after 24h, never touching the shared demo DB or another session's data. You must supply your own OpenAI
API key to query an uploaded database — it's used only for that session's requests and never persisted to disk;
the bundled demo continues to use the server's own key from .env, unaffected.
Import is deliberately best-effort, not all-or-nothing: dump syntax with no SQLite equivalent (MySQL's inline
KEY/INDEX clauses, Postgres's CREATE SEQUENCE/CREATE EXTENSION, session-config SET statements, schema
qualifiers like public.customers which SQLite would otherwise misparse as a cross-database reference) is
detected and skipped, with a report of what was skipped — not an opaque failure over one unsupported statement.
Capped at 20MB per upload; larger/async imports are a v2 concern.
Setup
python -m venv .venv
.venv/Scripts/pip install -r requirements.txt # Windows
# .venv/bin/pip install -r requirements.txt # macOS/Linux
cp .env.example .env # then edit .env with your real OPENAI_API_KEY
python scripts/build_graph.py # build graphrag/schema_graph.graphml from db/chinook.dbRunning it
MCP server (for Claude Desktop/Code — see .mcp.json, already configured for this project):
python server/main.pyWeb app:
uvicorn web.app:app --reload
# open http://127.0.0.1:8000CLI:
python scripts/run_pipeline.py "Which artist has the most albums?"
python scripts/query_graph.py "your question" # GraphRAG retrieval only, no LLMTesting
# Free, deterministic (no API calls):
python tests/test_sql_guard.py # 21 adversarial/legit SQL cases
python tests/test_query_executor.py # timeout, read-only backstop, row cap
python tests/test_retrieval.py # GraphRAG retrieval regression cases
python tests/test_pipeline_stages_offline.py
python tests/test_db_import.py # SQLite/MySQL/Postgres import + transpilation
# Real API calls (needs OPENAI_API_KEY):
python tests/test_plan_stage.py
python tests/test_generate_stage.py
python eval/run_eval.py # full 40-question golden setEval harness
eval/run_eval.py grades on executed results, not SQL text — two differently-written queries can both be correct, so it compares the pipeline's output rows against a hand-verified reference query's output (value-subset matching, tolerant of extra descriptive columns and float precision). 30 legitimate questions (counts, sums, joins, self-joins, nullable-FK handling, literal lookups) plus 10 adversarial (out-of-scope, prompt injection, destructive intent) — currently 40/40.
The first real eval run caught two genuine bugs (not eval-harness artifacts): Plan being too conservative about a
self-referencing FK relationship it had no schema access to verify, and Generate grouping by a non-unique display
column (Playlist.Name — two different playlists share that name in Chinook) instead of the primary key, silently
merging distinct rows. Both are fixed in the current prompts.
Wired into .github/workflows/eval.yml: deterministic tests run first (fail fast,
free), then the eval harness (needs an OPENAI_API_KEY repo secret), with the report uploaded as a CI artifact.
Deployment
docker build -t nl2sql-mcp-graphrag .
docker run -p 8000:8000 --env-file .env nl2sql-mcp-graphragrender.yaml is a Render blueprint (Docker runtime, free tier) — set OPENAI_API_KEY in the Render
dashboard after connecting the repo, it's intentionally not committed. Note: the Docker build hasn't been verified
in this environment (no Docker available) — test docker build . locally before deploying.
Known limitations / v2
No multi-tenancy — no row-level access control; deferred deliberately to keep v1 scoped.
No schema-drift detection —
scripts/build_graph.pymust be re-run manually after a schema change; no polling/webhook invalidation.Lexical/fuzzy schema matching, not embeddings — free and deterministic, but can't bridge true vocabulary gaps beyond the small synonym map in graphrag/matching.py (e.g. "revenue" →
Invoice.Totalis hardcoded, not learned). Swap-in point ismatch_schema_nodes()if eval ever shows this as a bottleneck.Retrieval is high-recall, not high-precision — an FK column like
Invoice.CustomerIdlegitimately contains the word "customer", so simple questions can pull in more tables than strictly needed. Generate has so far proven robust to this noise (see Phase 4 testing), but it's a known tradeoff, not a solved problem.Dump transpilation isn't guaranteed complete — best-effort, with a skip report for unsupported syntax (stored procedures, triggers, engine-specific types beyond what's already handled). Complex enterprise dumps may import partially. No background/async import, so upload is capped at 20MB and blocks the request until done.
No accounts — sessions are anonymous and expire after 24h; there's no way to return to an uploaded database later or share it across devices.
Project structure
db/ chinook.db (SQLite sample data)
server/ MCP server + AST guardrails (sql_guard.py, query_executor.py)
graphrag/ NetworkX schema graph, fuzzy matching, retrieval
pipeline/ 5-stage LLM pipeline (plan/retrieve/generate/validate/execute)
web/ FastAPI app + static frontend + multi-DB upload/import/sessions
eval/ golden question set, grader, eval runner
scripts/ CLI entrypoints (build_graph, query_graph, run_pipeline, inspect_schema)
tests/ deterministic test suitesThis server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-qualityFmaintenanceAn MCP server that provides safe, read-only access to SQLite databases through MCP. This server is built with the FastMCP framework, which enables LLMs to explore and query SQLite databases with built-in safety features and query validation.Last updated107
- Flicense-qualityDmaintenanceAn MCP server that enables AI assistants to query and interact with SQLite databases through natural language. It includes built-in security guardrails such as PII redaction, SQL injection blocking, and query rate limiting.Last updated
- Alicense-qualityCmaintenanceAn MCP server that enables AI agents to interact with SQLite databases by querying schemas, executing SQL, and inspecting table metadata. It supports safe database access through configurable read-only modes, query timeouts, and dry-run execution plans.Last updatedMIT
- AlicenseAqualityDmaintenanceMCP server providing LLMs with safe, read-only access to the Chinook SQLite sample database, including schema exploration and SQL query execution.Last updated11MIT
Related MCP Connectors
GibsonAI MCP server: manage your databases with natural language
Local-first RAG engine with MCP server for AI agent integration.
MCP server for AI dialogue using various LLM models via AceDataCloud
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/SatyamRudrakanthwar/SatyamRudrakanthwar-NL-to-SQL-based-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server