sql-specialist-mcp
Allows querying a SQLite database using natural language, translating questions into SQL and executing them against the database in a read-only, sandboxed manner.
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., "@sql-specialist-mcpShow me the total revenue per product category for the last quarter."
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.
sql-specialist-mcp
A small, fine-tuned open-weight model that answers natural-language questions about a SQLite database, served as an MCP tool any MCP client (Claude Desktop, Claude Code, custom agents) can call directly — backed by an execution-accuracy eval harness that benchmarks the specialist against prompting a frontier model on accuracy, latency, and cost.
The point of this project isn't "build a text-to-SQL demo" — it's to show the parts of LLM engineering that sit below prompting: taking a small model, adapting it to one task via LoRA, serving it efficiently, and proving with a real, execution-based eval that a cheap specialist is competitive with (or better than) prompting a frontier model for this narrow job.
Try the interactive demo — click through all 28 real eval questions and see the specialist's actual generated SQL, latency, and result rows next to the frontier baseline. No install required.
Why this exists
Most "AI portfolio" text-to-SQL projects are LangChain quickstarts. Two things here are meant to be different:
The eval is rigorous, not vibes. Every gold query is executed against the database at dataset-build time (139/139 validated), and scoring compares result sets, not query text — a semantically correct query with different column ordering still scores correct. A predictor that just echoes the gold SQL scores 100%; a predictor that always returns a trivially-wrong query scores 0%. Both are checked in as sanity tests (
tests/test_harness_oracle.py) so the harness's own correctness isn't assumed.It ships as something usable, not just a demo repo. The fine-tuned model is exposed as a real MCP tool (
nl_to_sql) — point Claude Desktop or Claude Code atmcp_server/server.pyand it can actually query the database as part of a conversation.
Related MCP server: mcp-sqlite-chat
Results
The full pipeline has been run end-to-end on real hardware, both sides: real
LoRA fine-tune, real merge, real GGUF quantization, real Ollama serving,
real eval — and a real frontier baseline against the live Claude API.
Base model: Qwen/Qwen2.5-Coder-0.5B-Instruct (chosen for a fast iteration
loop on a laptop; see Fine-tuning below for the 1.5B path).
Predictor | Accuracy | n | p50 latency | p95 latency | Cost / 1k calls |
frontier: Claude Haiku 4.5 (prompted) | 53.6% | 28 | 1055ms | 1884ms | $1.06 |
sql-specialist (fine-tuned, quantized, local) | 92.9% | 28 | 207ms | 371ms | $0.00 |
Read this with the caveat, not just the headline. I manually audited
every one of Claude Haiku's 13 measured "failures" against this eval set:
zero were SQL logic errors. All 13 were column-selection or row-order
convention mismatches — e.g. returning (name, email) when the gold answer
was just (name), or correct rows in a different order than an ORDER BY
the original question never actually specified. The strict execution-accuracy
metric (eval/execution.py compares result rows column-for-column) scores
those identically to a genuinely wrong query, which the fine-tuned specialist
never produces because it memorized this dataset's exact conventions from
111 training examples — something a frontier model prompted zero-shot has no
way to know. Full failure-by-failure taxonomy in COMPARISON.md.
So: the accuracy gap is real but partly an artifact of what the eval rewards, not purely a reasoning gap. The latency and cost gap is not an artifact — 207ms/local/free vs. 1055ms/$1.06-per-1k-calls is the actual, unhedged result of running a quantized 0.5B model locally instead of calling an API, and it's the comparison this project's premise actually rests on.
The specialist's own 2 failures (out of 28) were genuine logic errors, not
formatting mismatches — hallucinating a plausible orders.total column that
doesn't exist in this schema, and dropping a table qualifier in one
multi-table SELECT. Training converged cleanly over 3 epochs (eval loss
0.060 → 0.048 → 0.008), and the quantized model (988MB f16 →
373MB q4_k_m) serves through Ollama in ~200ms.
What's real here
Being upfront about this matters more than it looks — it's the difference between a project a recruiter can trust and one that reads like marketing.
The synthetic database and dataset are provably correct.
shopsphere.dbis seeded deterministically (seed=42); every one of the 139 gold (question, SQL) pairs indata/*.jsonlis generated from parameterized templates and executed against the real database at build time — a template that produces invalid SQL fails the build, it doesn't silently ship a bad label.The eval harness's correctness is itself tested, not assumed —
tests/test_harness_oracle.pyasserts an oracle predictor (returns gold SQL verbatim) scores exactly 100% and a deliberately-wrong predictor scores ~0%, before any real predictor's number is trusted.Execution accuracy, not string match.
eval/execution.pycompares result sets (order-insensitive unless the gold query hasORDER BY), so a query that's differently written but semantically equivalent still scores correct.The fine-tune is real, on this machine, verified converging. LoRA (8.8M trainable params, 1.75% of the model) over 3 epochs, eval loss dropping monotonically each epoch. See Engineering notes below for two real bugs hit and fixed along the way.
SQL execution is genuinely sandboxed, not just prompted to behave: read queries are validated against a regex allowlist and executed against a true read-only SQLite connection (
mode=roat the OS level) — a bug in the regex guard still can't result in a write. This matters beyond the eval harness, because the same guard runs in the MCP server, where the SQL comes from a model responding to an agent's question, not a curated eval set.The MCP server is a real, callable tool serving the real fine-tuned model, verified end-to-end:
nl_to_sql("Which employees have no manager assigned?")→ generates SQL via the quantized model over Ollama → executes it read-only → returns real rows → logs latency/cost to observability.Observability is self-built and dependency-free —
observability/logger.pylogs every call (latency, tokens, estimated cost, success/failure) to a local SQLite file, no external account needed, same pattern aspr-review-agent.The frontier baseline is real too —
eval/baseline_frontier.pyran against the live Claude API (Claude Haiku 4.5), not just imported cleanly. Its "failures" turned out to reveal a real eval-methodology finding — see Results above andCOMPARISON.mdfor the full manual failure audit.
Engineering notes: two real bugs found running this for real
Actually executing the fine-tune (rather than leaving it as "should work in theory") surfaced two genuine PyTorch memory bugs, both fixed in the current code:
MPS caching-allocator runaway. Training on Apple Silicon's MPS backend via
transformers.Trainercaused the process to balloon to 23GB RSS and hang, on dynamic per-batch padding — each distinct (batch, seq_len) shape gets its own memory pool in PyTorch's MPS allocator, which doesn't return freed memory to the OS. Fix:--device cpuoverride infinetune.py, and more fundamentally, fixed-length padding (below) so this class of bug can't recur on any backend.Trainer/DataLoaderoverhead, not the model. A direct forward+backward pass timed at 1.6s/example; the same computation throughtransformers.Trainerleft the process idle for minutes between logged steps with no corresponding compute. Root-caused by isolating the actual model+LoRA forward/backward with manual timing before assuming the bug was in model code. Fix: replacedTrainerwith a ~40-line manual training loop (training/finetune.py) — same LoRA setup, direct control over the batch loop, no unexplained overhead. Also switched batch collation from dynamic-per-batch to fixed-length padding (every batch shaped identically), which independently fixed the allocator-fragmentation pattern from bug #1.
Neither fix is a workaround bolted on top — both are visible in
training/finetune.py as the only implementation, not an alternate path.
Architecture
data/build_dataset.py ──▶ data/{train,eval}.jsonl (139 examples, template-generated,
every gold SQL executed at build time)
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
training/finetune.py eval/baseline_frontier.py tests/test_harness_oracle.py
(LoRA on a small (prompt Claude Haiku/ (sanity-checks the harness
open model) Sonnet as the baseline) itself before trusting scores)
│ │
▼ │
training/merge_and_quantize.py
│ │
▼ ▼
serving/ollama_predictor.py ──┴──▶ eval/harness.py ──▶ eval/report.py ──▶ COMPARISON.md
│ (execution-accuracy scoring,
│ same logic for every predictor)
▼
mcp_server/server.py (nl_to_sql tool -- installable in Claude Desktop/Code)
│
▼
observability/logger.py (latency, tokens, cost -- local SQLite, no external account)Project structure
schema/ synthetic "ShopSphere" e-commerce DB (7 tables) + seeded generator
data/ templated gold (question, SQL) dataset -- every query build-time validated
eval/ execution-accuracy harness, frontier baseline, comparison report
training/ LoRA fine-tuning pipeline + LoRA-merge/quantize script
serving/ Ollama-backed and in-process HF predictors, Ollama Modelfile template
mcp_server/ the installable MCP tool (nl_to_sql)
observability/ self-built call logging (latency/tokens/cost), no external account
tests/ harness sanity checks (oracle predictor must score 100%)Setup
python3.11 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt # base: anthropic, mcp, requests
python schema/generate_data.py # build the seeded database
python data/build_dataset.py # build + validate the gold dataset
python tests/test_harness_oracle.py # confirm the eval harness itself is soundrequirements-train.txt adds torch/transformers/peft/trl for the fine-tuning
path — heavier, kept separate so the eval/serving/MCP path installs fast.
Running the full pipeline
1. Frontier baseline (needs ANTHROPIC_API_KEY):
export ANTHROPIC_API_KEY="..."
python -m eval.baseline_frontier --model claude-haiku-4-5
# writes eval/results_claude-haiku-4-5.json2. Fine-tune the specialist (this is what was actually run to produce the results above — takes ~15 min of active compute on a laptop CPU, though wall clock varies a lot with system load; a GPU is much faster, see below):
pip install -r requirements-train.txt
python -m training.finetune --base-model Qwen/Qwen2.5-Coder-0.5B-Instruct --device cpu
python -m training.merge_and_quantize --base-model Qwen/Qwen2.5-Coder-0.5B-Instruct
# then follow the printed llama.cpp + ollama create instructions3. Score the specialist the same way as the baseline:
python -c "
from eval.harness import run_eval, report_to_dict
from serving.ollama_predictor import OllamaPredictor
import json
report = run_eval(OllamaPredictor())
json.dump(report_to_dict(report), open('eval/results_specialist.json', 'w'), indent=2)
"4. Generate the comparison report:
python -m eval.report eval/results_claude-haiku-4-5.json eval/results_specialist.jsonFine-tuning: scaling up
The results above use Qwen2.5-Coder-0.5B-Instruct on CPU, for a fast local
iteration loop. training/finetune.py --base-model accepts any HF causal-LM
repo (or a local directory) — Qwen2.5-Coder-1.5B-Instruct is a straightforward
swap for better quality, and a single cloud GPU (a T4 is enough for this
dataset size) trains either size in a couple of minutes instead of ~15:
pip install -r requirements-train.txt
python -m training.finetune \
--base-model Qwen/Qwen2.5-Coder-1.5B-Instruct \
--epochs 3--device {cuda,mps,cpu} overrides auto-detection. MPS is auto-detected on
Apple Silicon but not recommended for this task yet — see Engineering
notes above.
MCP server
# Backend defaults to a locally-served model via Ollama:
python -m mcp_server.server
# Or run against a prompted frontier model instead (no fine-tune needed --
# useful for trying the tool before training anything):
SQL_SPECIALIST_BACKEND=frontier SQL_SPECIALIST_MODEL=claude-haiku-4-5 \
ANTHROPIC_API_KEY=... python -m mcp_server.serverAdd to Claude Desktop's MCP config (claude_desktop_config.json):
{
"mcpServers": {
"sql-specialist": {
"command": "/absolute/path/to/sql-specialist-mcp/.venv/bin/python",
"args": ["-m", "mcp_server.server"],
"cwd": "/absolute/path/to/sql-specialist-mcp"
}
}
}Then ask Claude something like "Using the sql-specialist tool, which
customers have never placed an order?" — it calls nl_to_sql, gets back
real rows from the database, and answers grounded in the actual data.
Security notes
SQL execution is read-only at two independent layers: a regex guard rejecting anything but
SELECT/WITH, and a true OS-level read-only SQLite connection (file:...?mode=ro) as the backstop.The MCP server never executes anything the guard rejects, regardless of what the model or the calling agent asked for.
No secrets are stored in this repo.
ANTHROPIC_API_KEYis read from the environment only.
What I'd build next
Normalize the eval for column supersets — score a prediction correct if the gold-requested columns' values are present, rather than requiring an exact column-for-column match. This is the fix implied by the failure taxonomy in
COMPARISON.md; it would very likely close most of the measured 53.6%→92.9% gap and produce a comparison that isolates actual reasoning ability from convention-matching.Run
eval/baseline_frontier.pyagainst Claude Sonnet too, for a stronger-model comparison point (Haiku is the cheap/fast tier; Sonnet is the "how much does model strength alone close the gap" question).Fine-tune
Qwen2.5-Coder-1.5B-Instructon a GPU and compare accuracy against the 0.5B result (92.9%) to quantify the size/quality tradeoff directly.DPO targeting the specialist's two known failure modes (hallucinated columns, dropped table qualifiers in multi-joins) now that real failure data exists.
vLLM serving path for throughput comparison against the Ollama/GGUF path.
This 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
- FlicenseNot gradedqualityCmaintenanceEnables natural language database queries by combining Ollama's language models with SQLite database access through an MCP server.
- FlicenseNot gradedqualityCmaintenanceEnables querying and managing a SQLite database using natural language, with an MCP server and Groq LLM.
- FlicenseNot gradedqualityBmaintenanceEnables AI agents to query a SQLite database using natural language through the Model Context Protocol (MCP). Includes security guardrails that block destructive SQL operations.
- AlicenseNot gradedqualityDmaintenanceMCP tool server providing SQLite database access for AI agents.MIT
Related MCP Connectors
GibsonAI MCP server: manage your databases with natural language
Analytical memory for AI agents: a real Postgres queried in plain English over MCP. One command.
Free OpenAI-compatible inference with signed provenance receipts and 3 focused MCP tools.
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/Eshanya1/sql-specialist-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server